| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185 |
- /*!
- * @file Adafruit_SSD1306.cpp
- *
- * @mainpage Arduino library for monochrome OLEDs based on SSD1306 drivers.
- *
- * @section intro_sec Introduction
- *
- * This is documentation for Adafruit's SSD1306 library for monochrome
- * 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,
- * select, data/command) and optionally a reset pin. Hardware SPI or
- * 'bitbang' software SPI are both supported.
- *
- * Adafruit invests time and resources providing this open source code,
- * please support Adafruit and open-source hardware by purchasing
- * products from Adafruit!
- *
- * @section dependencies Dependencies
- *
- * This library depends on <a
- * href="https://github.com/adafruit/Adafruit-GFX-Library"> Adafruit_GFX</a>
- * being present on your system. Please make sure you have installed the latest
- * version before using this library.
- *
- * @section author Author
- *
- * Written by Limor Fried/Ladyada for Adafruit Industries, with
- * contributions from the open source community.
- *
- * @section license License
- *
- * BSD license, all text above, and the splash screen included below,
- * must be included in any redistribution.
- *
- */
- #ifdef __AVR__
- #include <avr/pgmspace.h>
- #elif defined(ESP8266) || defined(ESP32) || defined(ARDUINO_ARCH_RP2040)
- #include <pgmspace.h>
- #else
- #define pgm_read_byte(addr) \
- (*(const unsigned char *)(addr)) ///< PROGMEM workaround for non-AVR
- #endif
- #if !defined(__ARM_ARCH) && !defined(ENERGIA) && !defined(ESP8266) && \
- !defined(ESP32) && !defined(__arc__)
- #include <util/delay.h>
- #endif
- #include "Adafruit_SSD1306.h"
- #include "splash.h"
- #include <Adafruit_GFX.h>
- // SOME DEFINES AND STATIC VARIABLES USED INTERNALLY -----------------------
- #if defined(I2C_BUFFER_LENGTH)
- #define WIRE_MAX min(256, I2C_BUFFER_LENGTH) ///< Particle or similar Wire lib
- #elif defined(BUFFER_LENGTH)
- #define WIRE_MAX min(256, BUFFER_LENGTH) ///< AVR or similar Wire lib
- #elif defined(SERIAL_BUFFER_SIZE)
- #define WIRE_MAX \
- min(255, SERIAL_BUFFER_SIZE - 1) ///< Newer Wire uses RingBuffer
- #else
- #define WIRE_MAX 32 ///< Use common Arduino core default
- #endif
- #define ssd1306_swap(a, b) \
- (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b))) ///< No-temp-var swap operation
- #if ARDUINO >= 100
- #define WIRE_WRITE wire->write ///< Wire write function in recent Arduino lib
- #else
- #define WIRE_WRITE wire->send ///< Wire write function in older Arduino lib
- #endif
- #ifdef HAVE_PORTREG
- #define SSD1306_SELECT *csPort &= ~csPinMask; ///< Device select
- #define SSD1306_DESELECT *csPort |= csPinMask; ///< Device deselect
- #define SSD1306_MODE_COMMAND *dcPort &= ~dcPinMask; ///< Command mode
- #define SSD1306_MODE_DATA *dcPort |= dcPinMask; ///< Data mode
- #else
- #define SSD1306_SELECT digitalWrite(csPin, LOW); ///< Device select
- #define SSD1306_DESELECT digitalWrite(csPin, HIGH); ///< Device deselect
- #define SSD1306_MODE_COMMAND digitalWrite(dcPin, LOW); ///< Command mode
- #define SSD1306_MODE_DATA digitalWrite(dcPin, HIGH); ///< Data mode
- #endif
- #if (ARDUINO >= 157) && !defined(ARDUINO_STM32_FEATHER)
- #define SETWIRECLOCK wire->setClock(wireClk) ///< Set before I2C transfer
- #define RESWIRECLOCK wire->setClock(restoreClk) ///< Restore after I2C xfer
- #else // setClock() is not present in older Arduino Wire lib (or WICED)
- #define SETWIRECLOCK ///< Dummy stand-in define
- #define RESWIRECLOCK ///< keeps compiler happy
- #endif
- #if defined(SPI_HAS_TRANSACTION)
- #define SPI_TRANSACTION_START spi->beginTransaction(spiSettings) ///< Pre-SPI
- #define SPI_TRANSACTION_END spi->endTransaction() ///< Post-SPI
- #else // SPI transactions likewise not present in older Arduino SPI lib
- #define SPI_TRANSACTION_START ///< Dummy stand-in define
- #define SPI_TRANSACTION_END ///< keeps compiler happy
- #endif
- // The definition of 'transaction' is broadened a bit in the context of
- // this library -- referring not just to SPI transactions (if supported
- // in the version of the SPI library being used), but also chip select
- // (if SPI is being used, whether hardware or soft), and also to the
- // beginning and end of I2C transfers (the Wire clock may be sped up before
- // issuing data to the display, then restored to the default rate afterward
- // so other I2C device types still work). All of these are encapsulated
- // in the TRANSACTION_* macros.
- // Check first if Wire, then hardware SPI, then soft SPI:
- #define TRANSACTION_START \
- if (wire) { \
- SETWIRECLOCK; \
- } else { \
- if (spi) { \
- SPI_TRANSACTION_START; \
- } \
- SSD1306_SELECT; \
- } ///< Wire, SPI or bitbang transfer setup
- #define TRANSACTION_END \
- if (wire) { \
- RESWIRECLOCK; \
- } else { \
- SSD1306_DESELECT; \
- if (spi) { \
- SPI_TRANSACTION_END; \
- } \
- } ///< Wire, SPI or bitbang transfer end
- // CONSTRUCTORS, DESTRUCTOR ------------------------------------------------
- /*!
- @brief Constructor for I2C-interfaced SSD1306 displays.
- @param w
- Display width in pixels
- @param h
- Display height in pixels
- @param twi
- Pointer to an existing TwoWire instance (e.g. &Wire, the
- microcontroller's primary I2C bus).
- @param rst_pin
- Reset pin (using Arduino pin numbering), or -1 if not used
- (some displays might be wired to share the microcontroller's
- reset pin).
- @param clkDuring
- Speed (in Hz) for Wire transmissions in SSD1306 library calls.
- Defaults to 400000 (400 KHz), a known 'safe' value for most
- microcontrollers, and meets the SSD1306 datasheet spec.
- Some systems can operate I2C faster (800 KHz for ESP32, 1 MHz
- for many other 32-bit MCUs), and some (perhaps not all)
- SSD1306's can work with this -- so it's optionally be specified
- here and is not a default behavior. (Ignored if using pre-1.5.7
- Arduino software, which operates I2C at a fixed 100 KHz.)
- @param clkAfter
- Speed (in Hz) for Wire transmissions following SSD1306 library
- calls. Defaults to 100000 (100 KHz), the default Arduino Wire
- speed. This is done rather than leaving it at the 'during' speed
- because other devices on the I2C bus might not be compatible
- with the faster rate. (Ignored if using pre-1.5.7 Arduino
- software, which operates I2C at a fixed 100 KHz.)
- @return Adafruit_SSD1306 object.
- @note Call the object's begin() function before use -- buffer
- allocation is performed there!
- */
- Adafruit_SSD1306::Adafruit_SSD1306(uint8_t w, uint8_t h, TwoWire *twi,
- int8_t rst_pin, uint32_t clkDuring,
- uint32_t clkAfter)
- : Adafruit_GFX(w, h), spi(NULL), wire(twi ? twi : &Wire), buffer(NULL),
- mosiPin(-1), clkPin(-1), dcPin(-1), csPin(-1), rstPin(rst_pin)
- #if ARDUINO >= 157
- ,
- wireClk(clkDuring), restoreClk(clkAfter)
- #endif
- {
- }
- /*!
- @brief Constructor for SPI SSD1306 displays, using software (bitbang)
- SPI.
- @param w
- Display width in pixels
- @param h
- Display height in pixels
- @param mosi_pin
- MOSI (master out, slave in) pin (using Arduino pin numbering).
- This transfers serial data from microcontroller to display.
- @param sclk_pin
- SCLK (serial clock) pin (using Arduino pin numbering).
- This clocks each bit from MOSI.
- @param dc_pin
- Data/command pin (using Arduino pin numbering), selects whether
- display is receiving commands (low) or data (high).
- @param rst_pin
- Reset pin (using Arduino pin numbering), or -1 if not used
- (some displays might be wired to share the microcontroller's
- reset pin).
- @param cs_pin
- Chip-select pin (using Arduino pin numbering) for sharing the
- bus with other devices. Active low.
- @return Adafruit_SSD1306 object.
- @note Call the object's begin() function before use -- buffer
- allocation is performed there!
- */
- Adafruit_SSD1306::Adafruit_SSD1306(uint8_t w, uint8_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), spi(NULL), wire(NULL), buffer(NULL),
- mosiPin(mosi_pin), clkPin(sclk_pin), dcPin(dc_pin), csPin(cs_pin),
- rstPin(rst_pin) {}
- /*!
- @brief Constructor for SPI SSD1306 displays, using native hardware SPI.
- @param w
- Display width in pixels
- @param h
- Display height in pixels
- @param spi_ptr
- Pointer to an existing SPIClass instance (e.g. &SPI, the
- microcontroller's primary SPI bus).
- @param dc_pin
- Data/command pin (using Arduino pin numbering), selects whether
- display is receiving commands (low) or data (high).
- @param rst_pin
- Reset pin (using Arduino pin numbering), or -1 if not used
- (some displays might be wired to share the microcontroller's
- reset pin).
- @param cs_pin
- Chip-select pin (using Arduino pin numbering) for sharing the
- bus with other devices. Active low.
- @param bitrate
- SPI clock rate for transfers to this display. Default if
- unspecified is 8000000UL (8 MHz).
- @return Adafruit_SSD1306 object.
- @note Call the object's begin() function before use -- buffer
- allocation is performed there!
- */
- Adafruit_SSD1306::Adafruit_SSD1306(uint8_t w, uint8_t h, SPIClass *spi_ptr,
- int8_t dc_pin, int8_t rst_pin, int8_t cs_pin,
- uint32_t bitrate)
- : Adafruit_GFX(w, h), spi(spi_ptr ? spi_ptr : &SPI), wire(NULL),
- buffer(NULL), mosiPin(-1), clkPin(-1), dcPin(dc_pin), csPin(cs_pin),
- rstPin(rst_pin) {
- #ifdef SPI_HAS_TRANSACTION
- spiSettings = SPISettings(bitrate, MSBFIRST, SPI_MODE0);
- #endif
- }
- /*!
- @brief DEPRECATED constructor for SPI SSD1306 displays, using software
- (bitbang) SPI. Provided for older code to maintain compatibility
- with the current library. Screen size is determined by enabling
- one of the SSD1306_* size defines in Adafruit_SSD1306.h. New
- code should NOT use this.
- @param mosi_pin
- MOSI (master out, slave in) pin (using Arduino pin numbering).
- This transfers serial data from microcontroller to display.
- @param sclk_pin
- SCLK (serial clock) pin (using Arduino pin numbering).
- This clocks each bit from MOSI.
- @param dc_pin
- Data/command pin (using Arduino pin numbering), selects whether
- display is receiving commands (low) or data (high).
- @param rst_pin
- Reset pin (using Arduino pin numbering), or -1 if not used
- (some displays might be wired to share the microcontroller's
- reset pin).
- @param cs_pin
- Chip-select pin (using Arduino pin numbering) for sharing the
- bus with other devices. Active low.
- @return Adafruit_SSD1306 object.
- @note Call the object's begin() function before use -- buffer
- allocation is performed there!
- */
- Adafruit_SSD1306::Adafruit_SSD1306(int8_t mosi_pin, int8_t sclk_pin,
- int8_t dc_pin, int8_t rst_pin, int8_t cs_pin)
- : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT), spi(NULL), wire(NULL),
- buffer(NULL), mosiPin(mosi_pin), clkPin(sclk_pin), dcPin(dc_pin),
- csPin(cs_pin), rstPin(rst_pin) {}
- /*!
- @brief DEPRECATED constructor for SPI SSD1306 displays, using native
- hardware SPI. Provided for older code to maintain compatibility
- with the current library. Screen size is determined by enabling
- one of the SSD1306_* size defines in Adafruit_SSD1306.h. New
- code should NOT use this. Only the primary SPI bus is supported,
- and bitrate is fixed at 8 MHz.
- @param dc_pin
- Data/command pin (using Arduino pin numbering), selects whether
- display is receiving commands (low) or data (high).
- @param rst_pin
- Reset pin (using Arduino pin numbering), or -1 if not used
- (some displays might be wired to share the microcontroller's
- reset pin).
- @param cs_pin
- Chip-select pin (using Arduino pin numbering) for sharing the
- bus with other devices. Active low.
- @return Adafruit_SSD1306 object.
- @note Call the object's begin() function before use -- buffer
- allocation is performed there!
- */
- Adafruit_SSD1306::Adafruit_SSD1306(int8_t dc_pin, int8_t rst_pin, int8_t cs_pin)
- : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT), spi(&SPI), wire(NULL),
- buffer(NULL), mosiPin(-1), clkPin(-1), dcPin(dc_pin), csPin(cs_pin),
- rstPin(rst_pin) {
- #ifdef SPI_HAS_TRANSACTION
- spiSettings = SPISettings(8000000, MSBFIRST, SPI_MODE0);
- #endif
- }
- /*!
- @brief DEPRECATED constructor for I2C SSD1306 displays. Provided for
- older code to maintain compatibility with the current library.
- Screen size is determined by enabling one of the SSD1306_* size
- defines in Adafruit_SSD1306.h. New code should NOT use this.
- Only the primary I2C bus is supported.
- @param rst_pin
- Reset pin (using Arduino pin numbering), or -1 if not used
- (some displays might be wired to share the microcontroller's
- reset pin).
- @return Adafruit_SSD1306 object.
- @note Call the object's begin() function before use -- buffer
- allocation is performed there!
- */
- Adafruit_SSD1306::Adafruit_SSD1306(int8_t rst_pin)
- : Adafruit_GFX(SSD1306_LCDWIDTH, SSD1306_LCDHEIGHT), spi(NULL), wire(&Wire),
- buffer(NULL), mosiPin(-1), clkPin(-1), dcPin(-1), csPin(-1),
- rstPin(rst_pin) {}
- /*!
- @brief Destructor for Adafruit_SSD1306 object.
- */
- Adafruit_SSD1306::~Adafruit_SSD1306(void) {
- if (buffer) {
- free(buffer);
- buffer = NULL;
- }
- }
- // LOW-LEVEL UTILS ---------------------------------------------------------
- // Issue single byte out SPI, either soft or hardware as appropriate.
- // SPI transaction/selection must be performed in calling function.
- /*!
- @brief Write a single byte to the SPI port.
- @param d
- Data byte to be written.
- @return void
- @note See HAVE_PORTREG which defines if the method uses a port or bit-bang
- method
- */
- inline void Adafruit_SSD1306::SPIwrite(uint8_t d) {
- if (spi) {
- (void)spi->transfer(d);
- } else {
- for (uint8_t bit = 0x80; bit; bit >>= 1) {
- #ifdef HAVE_PORTREG
- if (d & bit)
- *mosiPort |= mosiPinMask;
- else
- *mosiPort &= ~mosiPinMask;
- *clkPort |= clkPinMask; // Clock high
- *clkPort &= ~clkPinMask; // Clock low
- #else
- digitalWrite(mosiPin, d & bit);
- digitalWrite(clkPin, HIGH);
- digitalWrite(clkPin, LOW);
- #endif
- }
- }
- }
- /*!
- @brief Issue single command to SSD1306, using I2C or hard/soft SPI as
- needed. Because command calls are often grouped, SPI transaction and
- selection must be started/ended in calling function for efficiency. This is a
- protected function, not exposed (see ssd1306_command() instead).
- @param c
- the command character to send to the display.
- Refer to ssd1306 data sheet for commands
- @return None (void).
- @note
- */
- void Adafruit_SSD1306::ssd1306_command1(uint8_t c) {
- if (wire) { // I2C
- wire->beginTransmission(i2caddr);
- WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0
- WIRE_WRITE(c);
- wire->endTransmission();
- } else { // SPI (hw or soft) -- transaction started in calling function
- SSD1306_MODE_COMMAND
- SPIwrite(c);
- }
- }
- /*!
- @brief Issue list of commands to SSD1306, same rules as above re:
- transactions. This is a protected function, not exposed.
- @param c
- pointer to list of commands
- @param n
- number of commands in the list
- @return None (void).
- @note
- */
- void Adafruit_SSD1306::ssd1306_commandList(const uint8_t *c, uint8_t n) {
- if (wire) { // I2C
- wire->beginTransmission(i2caddr);
- WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0
- uint16_t bytesOut = 1;
- while (n--) {
- if (bytesOut >= WIRE_MAX) {
- wire->endTransmission();
- wire->beginTransmission(i2caddr);
- WIRE_WRITE((uint8_t)0x00); // Co = 0, D/C = 0
- bytesOut = 1;
- }
- WIRE_WRITE(pgm_read_byte(c++));
- bytesOut++;
- }
- wire->endTransmission();
- } else { // SPI -- transaction started in calling function
- SSD1306_MODE_COMMAND
- while (n--)
- SPIwrite(pgm_read_byte(c++));
- }
- }
- // A public version of ssd1306_command1(), for existing user code that
- // might rely on that function. This encapsulates the command transfer
- // in a transaction start/end, similar to old library's handling of it.
- /*!
- @brief Issue a single low-level command directly to the SSD1306
- display, bypassing the library.
- @param c
- Command to issue (0x00 to 0xFF, see datasheet).
- @return None (void).
- */
- void Adafruit_SSD1306::ssd1306_command(uint8_t c) {
- TRANSACTION_START
- ssd1306_command1(c);
- TRANSACTION_END
- }
- // ALLOCATE & INIT DISPLAY -------------------------------------------------
- /*!
- @brief Allocate RAM for image buffer, initialize peripherals and pins.
- @param vcs
- VCC selection. Pass SSD1306_SWITCHCAPVCC to generate the display
- voltage (step up) from the 3.3V source, or SSD1306_EXTERNALVCC
- otherwise. Most situations with Adafruit SSD1306 breakouts will
- want SSD1306_SWITCHCAPVCC.
- @param addr
- I2C address of corresponding SSD1306 display (or pass 0 to use
- default of 0x3C for 128x32 display, 0x3D for all others).
- SPI displays (hardware or software) do not use addresses, but
- this argument is still required (pass 0 or any value really,
- it will simply be ignored). Default if unspecified is 0.
- @param reset
- If true, and if the reset pin passed to the constructor is
- valid, a hard reset will be performed before initializing the
- display. If using multiple SSD1306 displays on the same bus, and
- if they all share the same reset pin, you should only pass true
- on the first display being initialized, false on all others,
- else the already-initialized displays would be reset. Default if
- unspecified is true.
- @param periphBegin
- If true, and if a hardware peripheral is being used (I2C or SPI,
- but not software SPI), call that peripheral's begin() function,
- else (false) it has already been done in one's sketch code.
- Cases where false might be used include multiple displays or
- other devices sharing a common bus, or situations on some
- platforms where a nonstandard begin() function is available
- (e.g. a TwoWire interface on non-default pins, as can be done
- on the ESP8266 and perhaps others).
- @return true on successful allocation/init, false otherwise.
- Well-behaved code should check the return value before
- proceeding.
- @note MUST call this function before any drawing or updates!
- */
- bool Adafruit_SSD1306::begin(uint8_t vcs, uint8_t addr, bool reset,
- bool periphBegin) {
- if ((!buffer) && !(buffer = (uint8_t *)malloc(WIDTH * ((HEIGHT + 7) / 8))))
- return false;
- clearDisplay();
- #ifndef SSD1306_NO_SPLASH
- if (HEIGHT > 32) {
- drawBitmap((WIDTH - splash1_width) / 2, (HEIGHT - splash1_height) / 2,
- splash1_data, splash1_width, splash1_height, 1);
- } else {
- drawBitmap((WIDTH - splash2_width) / 2, (HEIGHT - splash2_height) / 2,
- splash2_data, splash2_width, splash2_height, 1);
- }
- #endif
- vccstate = vcs;
- // Setup pin directions
- if (wire) { // Using I2C
- // If I2C address is unspecified, use default
- // (0x3C for 32-pixel-tall displays, 0x3D for all others).
- i2caddr = addr ? addr : ((HEIGHT == 32) ? 0x3C : 0x3D);
- // TwoWire begin() function might be already performed by the calling
- // function if it has unusual circumstances (e.g. TWI variants that
- // can accept different SDA/SCL pins, or if two SSD1306 instances
- // with different addresses -- only a single begin() is needed).
- if (periphBegin)
- wire->begin();
- } else { // Using one of the SPI modes, either soft or hardware
- pinMode(dcPin, OUTPUT); // Set data/command pin as output
- pinMode(csPin, OUTPUT); // Same for chip select
- #ifdef HAVE_PORTREG
- dcPort = (PortReg *)portOutputRegister(digitalPinToPort(dcPin));
- dcPinMask = digitalPinToBitMask(dcPin);
- csPort = (PortReg *)portOutputRegister(digitalPinToPort(csPin));
- csPinMask = digitalPinToBitMask(csPin);
- #endif
- SSD1306_DESELECT
- if (spi) { // Hardware SPI
- // SPI peripheral begin same as wire check above.
- if (periphBegin)
- spi->begin();
- } else { // Soft SPI
- pinMode(mosiPin, OUTPUT); // MOSI and SCLK outputs
- pinMode(clkPin, OUTPUT);
- #ifdef HAVE_PORTREG
- mosiPort = (PortReg *)portOutputRegister(digitalPinToPort(mosiPin));
- mosiPinMask = digitalPinToBitMask(mosiPin);
- clkPort = (PortReg *)portOutputRegister(digitalPinToPort(clkPin));
- clkPinMask = digitalPinToBitMask(clkPin);
- *clkPort &= ~clkPinMask; // Clock low
- #else
- digitalWrite(clkPin, LOW); // Clock low
- #endif
- }
- }
- // Reset SSD1306 if requested and reset pin specified in constructor
- if (reset && (rstPin >= 0)) {
- pinMode(rstPin, OUTPUT);
- digitalWrite(rstPin, HIGH);
- delay(1); // VDD goes high at start, pause for 1 ms
- digitalWrite(rstPin, LOW); // Bring reset low
- delay(10); // Wait 10 ms
- digitalWrite(rstPin, HIGH); // Bring out of reset
- }
- TRANSACTION_START
- // Init sequence
- static const uint8_t PROGMEM init1[] = {SSD1306_DISPLAYOFF, // 0xAE
- SSD1306_SETDISPLAYCLOCKDIV, // 0xD5
- 0x80, // the suggested ratio 0x80
- SSD1306_SETMULTIPLEX}; // 0xA8
- ssd1306_commandList(init1, sizeof(init1));
- ssd1306_command1(HEIGHT - 1);
- static const uint8_t PROGMEM init2[] = {SSD1306_SETDISPLAYOFFSET, // 0xD3
- 0x0, // no offset
- SSD1306_SETSTARTLINE | 0x0, // line #0
- SSD1306_CHARGEPUMP}; // 0x8D
- ssd1306_commandList(init2, sizeof(init2));
- ssd1306_command1((vccstate == SSD1306_EXTERNALVCC) ? 0x10 : 0x14);
- static const uint8_t PROGMEM init3[] = {SSD1306_MEMORYMODE, // 0x20
- 0x00, // 0x0 act like ks0108
- SSD1306_SEGREMAP | 0x1,
- SSD1306_COMSCANDEC};
- ssd1306_commandList(init3, sizeof(init3));
- uint8_t comPins = 0x02;
- contrast = 0x8F;
- if ((WIDTH == 128) && (HEIGHT == 32)) {
- comPins = 0x02;
- contrast = 0x8F;
- } else if ((WIDTH == 128) && (HEIGHT == 64)) {
- comPins = 0x12;
- contrast = (vccstate == SSD1306_EXTERNALVCC) ? 0x9F : 0xCF;
- } else if ((WIDTH == 96) && (HEIGHT == 16)) {
- comPins = 0x2; // ada x12
- contrast = (vccstate == SSD1306_EXTERNALVCC) ? 0x10 : 0xAF;
- } else {
- // Other screen varieties -- TBD
- }
- ssd1306_command1(SSD1306_SETCOMPINS);
- ssd1306_command1(comPins);
- ssd1306_command1(SSD1306_SETCONTRAST);
- ssd1306_command1(contrast);
- ssd1306_command1(SSD1306_SETPRECHARGE); // 0xd9
- ssd1306_command1((vccstate == SSD1306_EXTERNALVCC) ? 0x22 : 0xF1);
- static const uint8_t PROGMEM init5[] = {
- SSD1306_SETVCOMDETECT, // 0xDB
- 0x40,
- SSD1306_DISPLAYALLON_RESUME, // 0xA4
- SSD1306_NORMALDISPLAY, // 0xA6
- SSD1306_DEACTIVATE_SCROLL,
- SSD1306_DISPLAYON}; // Main screen turn on
- ssd1306_commandList(init5, sizeof(init5));
- TRANSACTION_END
- return true; // Success
- }
- // DRAWING FUNCTIONS -------------------------------------------------------
- /*!
- @brief Set/clear/invert a single pixel. This is also invoked by the
- Adafruit_GFX library in generating many higher-level graphics
- primitives.
- @param x
- Column of display -- 0 at left to (screen width - 1) at right.
- @param y
- Row of display -- 0 at top to (screen height -1) at bottom.
- @param color
- Pixel color, one of: SSD1306_BLACK, SSD1306_WHITE or
- SSD1306_INVERSE.
- @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_SSD1306::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:
- ssd1306_swap(x, y);
- x = WIDTH - x - 1;
- break;
- case 2:
- x = WIDTH - x - 1;
- y = HEIGHT - y - 1;
- break;
- case 3:
- ssd1306_swap(x, y);
- y = HEIGHT - y - 1;
- break;
- }
- switch (color) {
- case SSD1306_WHITE:
- buffer[x + (y / 8) * WIDTH] |= (1 << (y & 7));
- break;
- case SSD1306_BLACK:
- buffer[x + (y / 8) * WIDTH] &= ~(1 << (y & 7));
- break;
- case SSD1306_INVERSE:
- buffer[x + (y / 8) * WIDTH] ^= (1 << (y & 7));
- break;
- }
- }
- }
- /*!
- @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_SSD1306::clearDisplay(void) {
- memset(buffer, 0, WIDTH * ((HEIGHT + 7) / 8));
- }
- /*!
- @brief Draw a horizontal line. This is also invoked by the Adafruit_GFX
- library in generating many higher-level graphics primitives.
- @param x
- Leftmost column -- 0 at left to (screen width - 1) at right.
- @param y
- Row of display -- 0 at top to (screen height -1) at bottom.
- @param w
- Width of line, in pixels.
- @param color
- Line color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERSE.
- @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_SSD1306::drawFastHLine(int16_t x, int16_t y, int16_t w,
- uint16_t color) {
- bool bSwap = false;
- switch (rotation) {
- case 1:
- // 90 degree rotation, swap x & y for rotation, then invert x
- bSwap = true;
- ssd1306_swap(x, y);
- x = WIDTH - x - 1;
- break;
- case 2:
- // 180 degree rotation, invert x and y, then shift y around for height.
- x = WIDTH - x - 1;
- y = HEIGHT - y - 1;
- x -= (w - 1);
- break;
- case 3:
- // 270 degree rotation, swap x & y for rotation,
- // then invert y and adjust y for w (not to become h)
- bSwap = true;
- ssd1306_swap(x, y);
- y = HEIGHT - y - 1;
- y -= (w - 1);
- break;
- }
- if (bSwap)
- drawFastVLineInternal(x, y, w, color);
- else
- drawFastHLineInternal(x, y, w, color);
- }
- /*!
- @brief Draw a horizontal line with a width and color. Used by public
- methods drawFastHLine,drawFastVLine
- @param x
- Leftmost column -- 0 at left to (screen width - 1) at right.
- @param y
- Row of display -- 0 at top to (screen height -1) at bottom.
- @param w
- Width of line, in pixels.
- @param color
- Line color, one of: SSD1306_BLACK, SSD1306_WHITE or
- SSD1306_INVERSE.
- @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_SSD1306::drawFastHLineInternal(int16_t x, int16_t y, int16_t w,
- uint16_t color) {
- if ((y >= 0) && (y < HEIGHT)) { // Y coord in bounds?
- if (x < 0) { // Clip left
- w += x;
- x = 0;
- }
- if ((x + w) > WIDTH) { // Clip right
- w = (WIDTH - x);
- }
- if (w > 0) { // Proceed only if width is positive
- uint8_t *pBuf = &buffer[(y / 8) * WIDTH + x], mask = 1 << (y & 7);
- switch (color) {
- case SSD1306_WHITE:
- while (w--) {
- *pBuf++ |= mask;
- };
- break;
- case SSD1306_BLACK:
- mask = ~mask;
- while (w--) {
- *pBuf++ &= mask;
- };
- break;
- case SSD1306_INVERSE:
- while (w--) {
- *pBuf++ ^= mask;
- };
- break;
- }
- }
- }
- }
- /*!
- @brief Draw a vertical line. This is also invoked by the Adafruit_GFX
- library in generating many higher-level graphics primitives.
- @param x
- Column of display -- 0 at left to (screen width -1) at right.
- @param y
- Topmost row -- 0 at top to (screen height - 1) at bottom.
- @param h
- Height of line, in pixels.
- @param color
- Line color, one of: SSD1306_BLACK, SSD1306_WHITE or SSD1306_INVERSE.
- @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_SSD1306::drawFastVLine(int16_t x, int16_t y, int16_t h,
- uint16_t color) {
- bool bSwap = false;
- switch (rotation) {
- case 1:
- // 90 degree rotation, swap x & y for rotation,
- // then invert x and adjust x for h (now to become w)
- bSwap = true;
- ssd1306_swap(x, y);
- x = WIDTH - x - 1;
- x -= (h - 1);
- break;
- case 2:
- // 180 degree rotation, invert x and y, then shift y around for height.
- x = WIDTH - x - 1;
- y = HEIGHT - y - 1;
- y -= (h - 1);
- break;
- case 3:
- // 270 degree rotation, swap x & y for rotation, then invert y
- bSwap = true;
- ssd1306_swap(x, y);
- y = HEIGHT - y - 1;
- break;
- }
- if (bSwap)
- drawFastHLineInternal(x, y, h, color);
- else
- drawFastVLineInternal(x, y, h, color);
- }
- /*!
- @brief Draw a vertical line with a width and color. Used by public method
- drawFastHLine,drawFastVLine
- @param x
- Leftmost column -- 0 at left to (screen width - 1) at right.
- @param __y
- Row of display -- 0 at top to (screen height -1) at bottom.
- @param __h height of the line in pixels
- @param color
- Line color, one of: SSD1306_BLACK, SSD1306_WHITE or
- SSD1306_INVERSE.
- @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_SSD1306::drawFastVLineInternal(int16_t x, int16_t __y,
- int16_t __h, uint16_t color) {
- if ((x >= 0) && (x < WIDTH)) { // X coord in bounds?
- if (__y < 0) { // Clip top
- __h += __y;
- __y = 0;
- }
- if ((__y + __h) > HEIGHT) { // Clip bottom
- __h = (HEIGHT - __y);
- }
- if (__h > 0) { // Proceed only if height is now positive
- // this display doesn't need ints for coordinates,
- // use local byte registers for faster juggling
- uint8_t y = __y, h = __h;
- uint8_t *pBuf = &buffer[(y / 8) * WIDTH + x];
- // do the first partial byte, if necessary - this requires some masking
- uint8_t mod = (y & 7);
- if (mod) {
- // mask off the high n bits we want to set
- mod = 8 - mod;
- // note - lookup table results in a nearly 10% performance
- // improvement in fill* functions
- // uint8_t mask = ~(0xFF >> mod);
- static const uint8_t PROGMEM premask[8] = {0x00, 0x80, 0xC0, 0xE0,
- 0xF0, 0xF8, 0xFC, 0xFE};
- uint8_t mask = pgm_read_byte(&premask[mod]);
- // adjust the mask if we're not going to reach the end of this byte
- if (h < mod)
- mask &= (0XFF >> (mod - h));
- switch (color) {
- case SSD1306_WHITE:
- *pBuf |= mask;
- break;
- case SSD1306_BLACK:
- *pBuf &= ~mask;
- break;
- case SSD1306_INVERSE:
- *pBuf ^= mask;
- break;
- }
- pBuf += WIDTH;
- }
- if (h >= mod) { // More to go?
- h -= mod;
- // Write solid bytes while we can - effectively 8 rows at a time
- if (h >= 8) {
- if (color == SSD1306_INVERSE) {
- // separate copy of the code so we don't impact performance of
- // black/white write version with an extra comparison per loop
- do {
- *pBuf ^= 0xFF; // Invert byte
- pBuf += WIDTH; // Advance pointer 8 rows
- h -= 8; // Subtract 8 rows from height
- } while (h >= 8);
- } else {
- // store a local value to work with
- uint8_t val = (color != SSD1306_BLACK) ? 255 : 0;
- do {
- *pBuf = val; // Set byte
- pBuf += WIDTH; // Advance pointer 8 rows
- h -= 8; // Subtract 8 rows from height
- } while (h >= 8);
- }
- }
- if (h) { // Do the final partial byte, if necessary
- mod = h & 7;
- // this time we want to mask the low bits of the byte,
- // vs the high bits we did above
- // uint8_t mask = (1 << mod) - 1;
- // note - lookup table results in a nearly 10% performance
- // improvement in fill* functions
- static const uint8_t PROGMEM postmask[8] = {0x00, 0x01, 0x03, 0x07,
- 0x0F, 0x1F, 0x3F, 0x7F};
- uint8_t mask = pgm_read_byte(&postmask[mod]);
- switch (color) {
- case SSD1306_WHITE:
- *pBuf |= mask;
- break;
- case SSD1306_BLACK:
- *pBuf &= ~mask;
- break;
- case SSD1306_INVERSE:
- *pBuf ^= mask;
- break;
- }
- }
- }
- } // endif positive height
- } // endif x in bounds
- }
- /*!
- @brief Return color of a single pixel in display buffer.
- @param x
- Column of display -- 0 at left to (screen width - 1) at right.
- @param y
- Row of display -- 0 at top to (screen height -1) at bottom.
- @return true if pixel is set (usually SSD1306_WHITE, unless display invert
- mode is enabled), false if clear (SSD1306_BLACK).
- @note Reads from buffer contents; may not reflect current contents of
- screen if display() has not been called.
- */
- bool Adafruit_SSD1306::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:
- ssd1306_swap(x, y);
- x = WIDTH - x - 1;
- break;
- case 2:
- x = WIDTH - x - 1;
- y = HEIGHT - y - 1;
- break;
- case 3:
- ssd1306_swap(x, y);
- y = HEIGHT - y - 1;
- break;
- }
- return (buffer[x + (y / 8) * WIDTH] & (1 << (y & 7)));
- }
- return false; // Pixel out of bounds
- }
- /*!
- @brief Get base address of display buffer for direct reading or writing.
- @return Pointer to an unsigned 8-bit array, column-major, columns padded
- to full byte boundary if needed.
- */
- uint8_t *Adafruit_SSD1306::getBuffer(void) { return buffer; }
- // REFRESH DISPLAY ---------------------------------------------------------
- /*!
- @brief Push data currently in RAM to SSD1306 display.
- @return None (void).
- @note Drawing operations are not visible until this function is
- called. Call after each graphics command, or after a whole set
- of graphics commands, as best needed by one's own application.
- */
- void Adafruit_SSD1306::display(void) {
- TRANSACTION_START
- static const uint8_t PROGMEM dlist1[] = {
- SSD1306_PAGEADDR,
- 0, // Page start address
- 0xFF, // Page end (not really, but works here)
- SSD1306_COLUMNADDR, 0}; // Column start address
- ssd1306_commandList(dlist1, sizeof(dlist1));
- ssd1306_command1(WIDTH - 1); // Column end address
- #if defined(ESP8266)
- // ESP8266 needs a periodic yield() call to avoid watchdog reset.
- // With the limited size of SSD1306 displays, and the fast bitrate
- // being used (1 MHz or more), I think one yield() immediately before
- // a screen write and one immediately after should cover it. But if
- // not, if this becomes a problem, yields() might be added in the
- // 32-byte transfer condition below.
- yield();
- #endif
- uint16_t count = WIDTH * ((HEIGHT + 7) / 8);
- uint8_t *ptr = buffer;
- if (wire) { // I2C
- wire->beginTransmission(i2caddr);
- WIRE_WRITE((uint8_t)0x40);
- uint16_t bytesOut = 1;
- while (count--) {
- if (bytesOut >= WIRE_MAX) {
- wire->endTransmission();
- wire->beginTransmission(i2caddr);
- WIRE_WRITE((uint8_t)0x40);
- bytesOut = 1;
- }
- WIRE_WRITE(*ptr++);
- bytesOut++;
- }
- wire->endTransmission();
- } else { // SPI
- SSD1306_MODE_DATA
- while (count--)
- SPIwrite(*ptr++);
- }
- TRANSACTION_END
- #if defined(ESP8266)
- yield();
- #endif
- }
- // SCROLLING FUNCTIONS -----------------------------------------------------
- /*!
- @brief Activate a right-handed scroll for all or part of the display.
- @param start
- First row.
- @param stop
- Last row.
- @return None (void).
- */
- // To scroll the whole display, run: display.startscrollright(0x00, 0x0F)
- void Adafruit_SSD1306::startscrollright(uint8_t start, uint8_t stop) {
- TRANSACTION_START
- static const uint8_t PROGMEM scrollList1a[] = {
- SSD1306_RIGHT_HORIZONTAL_SCROLL, 0X00};
- ssd1306_commandList(scrollList1a, sizeof(scrollList1a));
- ssd1306_command1(start);
- ssd1306_command1(0X00);
- ssd1306_command1(stop);
- static const uint8_t PROGMEM scrollList1b[] = {0X00, 0XFF,
- SSD1306_ACTIVATE_SCROLL};
- ssd1306_commandList(scrollList1b, sizeof(scrollList1b));
- TRANSACTION_END
- }
- /*!
- @brief Activate a left-handed scroll for all or part of the display.
- @param start
- First row.
- @param stop
- Last row.
- @return None (void).
- */
- // To scroll the whole display, run: display.startscrollleft(0x00, 0x0F)
- void Adafruit_SSD1306::startscrollleft(uint8_t start, uint8_t stop) {
- TRANSACTION_START
- static const uint8_t PROGMEM scrollList2a[] = {SSD1306_LEFT_HORIZONTAL_SCROLL,
- 0X00};
- ssd1306_commandList(scrollList2a, sizeof(scrollList2a));
- ssd1306_command1(start);
- ssd1306_command1(0X00);
- ssd1306_command1(stop);
- static const uint8_t PROGMEM scrollList2b[] = {0X00, 0XFF,
- SSD1306_ACTIVATE_SCROLL};
- ssd1306_commandList(scrollList2b, sizeof(scrollList2b));
- TRANSACTION_END
- }
- /*!
- @brief Activate a diagonal scroll for all or part of the display.
- @param start
- First row.
- @param stop
- Last row.
- @return None (void).
- */
- // display.startscrolldiagright(0x00, 0x0F)
- void Adafruit_SSD1306::startscrolldiagright(uint8_t start, uint8_t stop) {
- TRANSACTION_START
- static const uint8_t PROGMEM scrollList3a[] = {
- SSD1306_SET_VERTICAL_SCROLL_AREA, 0X00};
- ssd1306_commandList(scrollList3a, sizeof(scrollList3a));
- ssd1306_command1(HEIGHT);
- static const uint8_t PROGMEM scrollList3b[] = {
- SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL, 0X00};
- ssd1306_commandList(scrollList3b, sizeof(scrollList3b));
- ssd1306_command1(start);
- ssd1306_command1(0X00);
- ssd1306_command1(stop);
- static const uint8_t PROGMEM scrollList3c[] = {0X01, SSD1306_ACTIVATE_SCROLL};
- ssd1306_commandList(scrollList3c, sizeof(scrollList3c));
- TRANSACTION_END
- }
- /*!
- @brief Activate alternate diagonal scroll for all or part of the display.
- @param start
- First row.
- @param stop
- Last row.
- @return None (void).
- */
- // To scroll the whole display, run: display.startscrolldiagleft(0x00, 0x0F)
- void Adafruit_SSD1306::startscrolldiagleft(uint8_t start, uint8_t stop) {
- TRANSACTION_START
- static const uint8_t PROGMEM scrollList4a[] = {
- SSD1306_SET_VERTICAL_SCROLL_AREA, 0X00};
- ssd1306_commandList(scrollList4a, sizeof(scrollList4a));
- ssd1306_command1(HEIGHT);
- static const uint8_t PROGMEM scrollList4b[] = {
- SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL, 0X00};
- ssd1306_commandList(scrollList4b, sizeof(scrollList4b));
- ssd1306_command1(start);
- ssd1306_command1(0X00);
- ssd1306_command1(stop);
- static const uint8_t PROGMEM scrollList4c[] = {0X01, SSD1306_ACTIVATE_SCROLL};
- ssd1306_commandList(scrollList4c, sizeof(scrollList4c));
- TRANSACTION_END
- }
- /*!
- @brief Cease a previously-begun scrolling action.
- @return None (void).
- */
- void Adafruit_SSD1306::stopscroll(void) {
- TRANSACTION_START
- ssd1306_command1(SSD1306_DEACTIVATE_SCROLL);
- TRANSACTION_END
- }
- // OTHER HARDWARE SETTINGS -------------------------------------------------
- /*!
- @brief Enable or disable display invert mode (white-on-black vs
- black-on-white).
- @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 SSD1306_BLACK (value 0) pixels will actually draw
- white, SSD1306_WHITE (value 1) will draw black.
- */
- void Adafruit_SSD1306::invertDisplay(bool i) {
- TRANSACTION_START
- ssd1306_command1(i ? SSD1306_INVERTDISPLAY : SSD1306_NORMALDISPLAY);
- TRANSACTION_END
- }
- /*!
- @brief Dim the display.
- @param dim
- true to enable lower brightness mode, false for full brightness.
- @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_SSD1306::dim(bool dim) {
- // the range of contrast to too small to be really useful
- // it is useful to dim the display
- TRANSACTION_START
- ssd1306_command1(SSD1306_SETCONTRAST);
- ssd1306_command1(dim ? 0 : contrast);
- TRANSACTION_END
- }
|