diff --git a/I2CManager.cpp b/I2CManager.cpp index 676f2ce..27e85f1 100644 --- a/I2CManager.cpp +++ b/I2CManager.cpp @@ -17,6 +17,8 @@ * along with CommandStation. If not, see . */ +#include +#include #include "I2CManager.h" // If not already initialised, initialise I2C (wire). @@ -48,10 +50,80 @@ void I2CManagerClass::forceClock(uint32_t speed) { // Check if specified I2C address is responding. // Returns 0 if OK, or error code. -uint8_t I2CManagerClass::exists(uint8_t address) { +uint8_t I2CManagerClass::checkAddress(uint8_t address) { begin(); Wire.beginTransmission(address); return Wire.endTransmission(); } +bool I2CManagerClass::exists(uint8_t address) { + return checkAddress(address)==0; +} + +// Write a complete transmission to I2C using a supplied buffer of data +uint8_t I2CManagerClass::write(uint8_t address, const uint8_t buffer[], uint8_t size) { + Wire.beginTransmission(address); + Wire.write(buffer, size); + return Wire.endTransmission(); +} + +// Write a complete transmission to I2C using a supplied buffer of data in Flash +uint8_t I2CManagerClass::write_P(uint8_t address, const uint8_t buffer[], uint8_t size) { + uint8_t ramBuffer[size]; + memcpy_P(ramBuffer, buffer, size); + return write(address, ramBuffer, size); +} + + +// Write a complete transmission to I2C using a list of data +uint8_t I2CManagerClass::write(uint8_t address, int nBytes, ...) { + uint8_t buffer[nBytes]; + va_list args; + va_start(args, nBytes); + for (uint8_t i=0; i 0) { + Wire.beginTransmission(address); + Wire.write(writeBuffer, writeSize); + Wire.endTransmission(false); // Don't free bus yet + } + Wire.requestFrom(address, readSize); + uint8_t nBytes = 0; + while (Wire.available() && nBytes < readSize) + readBuffer[nBytes++] = Wire.read(); + return nBytes; +} + +// Overload of read() to allow command to be specified as a series of bytes. +uint8_t I2CManagerClass::read(uint8_t address, uint8_t readBuffer[], uint8_t readSize, + uint8_t writeSize, ...) { + va_list args; + // Copy the series of bytes into an array. + va_start(args, writeSize); + uint8_t writeBuffer[writeSize]; + for (uint8_t i=0; i +#include "FSH.h" /* * Helper class to manage access to the I2C 'Wire' subsystem. @@ -48,12 +48,27 @@ public: // Force clock speed void forceClock(uint32_t speed); // Check if specified I2C address is responding. - uint8_t exists(uint8_t address); + uint8_t checkAddress(uint8_t address); + bool exists(uint8_t address); + // Write a complete transmission to I2C from an array in RAM + uint8_t write(uint8_t address, const uint8_t buffer[], uint8_t size); + // Write a complete transmission to I2C from an array in Flash + uint8_t write_P(uint8_t address, const uint8_t buffer[], uint8_t size); + // Write a transmission to I2C from a list of bytes. + uint8_t write(uint8_t address, int nBytes, ...); + // Write a command from an array in RAM and read response + uint8_t read(uint8_t address, uint8_t writeBuffer[], uint8_t writeSize, + uint8_t readBuffer[], uint8_t readSize); + // Write a command from an arbitrary list of bytes and read response + uint8_t read(uint8_t address, uint8_t readBuffer[], uint8_t readSize, + uint8_t writeSize, ...); + // Write a null command and read the response. + uint8_t read(uint8_t address, uint8_t readBuffer[], uint8_t readSize); private: bool _beginCompleted = false; bool _clockSpeedFixed = false; - uint32_t _clockSpeed = 1000000L; // 1MHz max on Arduino. + uint32_t _clockSpeed = 400000L; // 400kHz max on Arduino. }; extern I2CManagerClass I2CManager; diff --git a/LCD_OLED.h b/LCD_OLED.h index afd79fd..a849a20 100644 --- a/LCD_OLED.h +++ b/LCD_OLED.h @@ -24,7 +24,6 @@ #include "I2CManager.h" #include "SSD1306Ascii.h" -#include "Wire.h" SSD1306AsciiWire LCDDriver; // DEVICE SPECIFIC LCDDisplay Implementation for OLED @@ -34,10 +33,9 @@ LCDDisplay::LCDDisplay() { I2CManager.begin(); I2CManager.setClock(400000L); // Set max supported I2C speed for (byte address = 0x3c; address <= 0x3d; address++) { - byte error = I2CManager.exists(address); - if (!error) { + if (I2CManager.exists(address)) { // Device found - DIAG(F("OLED display found at 0x%x"), address); + DIAG(F("\nOLED display found at 0x%x"), address); interfake(OLED_DRIVER, 0); const DevType *devType; if (lcdCols == 132) @@ -65,7 +63,7 @@ void LCDDisplay::interfake(int p1, int p2, int p3) { void LCDDisplay::clearNative() { LCDDriver.clear(); } void LCDDisplay::setRowNative(byte row) { - // Positions text write to start of row 1..n and clears previous text + // Positions text write to start of row 1..n int y = row; LCDDriver.setCursor(0, y); } diff --git a/LiquidCrystal_I2C.cpp b/LiquidCrystal_I2C.cpp index 751892a..90aac56 100644 --- a/LiquidCrystal_I2C.cpp +++ b/LiquidCrystal_I2C.cpp @@ -15,32 +15,13 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with CommandStation. If not, see . + * along with CommandStation-EX. If not, see . */ +#include #include "LiquidCrystal_I2C.h" #include "I2CManager.h" -#include -#if defined(ARDUINO) && ARDUINO >= 100 - -#include "Arduino.h" - -#define printIIC(args) Wire.write(args) -inline size_t LiquidCrystal_I2C::write(uint8_t value) { - send(value, Rs); - return 1; -} - -#else -#include "WProgram.h" - -#define printIIC(args) Wire.send(args) -inline void LiquidCrystal_I2C::write(uint8_t value) { send(value, Rs); } - -#endif -#include "Wire.h" - // When the display powers up, it is configured as follows: // // 1. Display clear @@ -72,7 +53,7 @@ void LiquidCrystal_I2C::init() { init_priv(); } void LiquidCrystal_I2C::init_priv() { I2CManager.begin(); - I2CManager.setClock(100000L); // PCF8574 is limited to 100kHz. + I2CManager.setClock(100000L); // PCF8574 is spec'd to 100kHz. _displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS; begin(_cols, _rows); @@ -85,10 +66,9 @@ void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines) { _numlines = lines; (void)cols; // Suppress compiler warning. - // SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION! // according to datasheet, we need at least 40ms after power rises above 2.7V // before sending commands. Arduino can turn on way befer 4.5V so we'll allow - // 100 milliseconds after pulling both RS and R/W and backlight pin low + // 100 milliseconds after pulling both RS and R/W and backlight pin low expanderWrite( _backlightval); // reset expander and turn backlight off (Bit 8 =1) delay(100); @@ -167,23 +147,11 @@ void LiquidCrystal_I2C::backlight(void) { expanderWrite(0); } -void LiquidCrystal_I2C::setBacklight(uint8_t new_val) { - if (new_val) { - backlight(); // turn backlight on - } else { - noBacklight(); // turn backlight off - } -} - -void LiquidCrystal_I2C::printstr(const char c[]) { - // This function is not identical to the function used for "real" I2C displays - // it's here so the user sketch doesn't have to be changed - print(c); -} - /*********** mid level commands, for sending data/cmds */ -inline void LiquidCrystal_I2C::command(uint8_t value) { send(value, 0); } +inline void LiquidCrystal_I2C::command(uint8_t value) { + send(value, 0); +} /************ low level data pushing commands **********/ @@ -209,37 +177,37 @@ inline void LiquidCrystal_I2C::command(uint8_t value) { send(value, 0); } * transmission and start another one is a stop bit, a start bit, 8 address bits, * an ack, 8 data bits and another ack; this is at least 20 bits, i.e. >50us * at 400kHz and >200us at 100kHz. Therefore, we don't need additional delay. + * + * Similarly, the Enable must be set/reset for at least 450ns. This is + * well within the I2C clock cycle time of 2.5us at 400kHz. Data is clocked in + * to the HD44780 on the trailing edge of the Enable pin, so we set the Enable + * as we present the data, then in the next byte we reset Enable without changing + * the data. */ -// write either command or data (8 bits) to the HD44780 as -// a single I2C transmission. +// write either command or data (8 bits) to the HD44780 LCD controller as +// a single I2C transmission. void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) { - uint8_t highnib = value & 0xf0; - uint8_t lownib = (value << 4) & 0xf0; + mode |= _backlightval; + uint8_t highnib = (value & 0xf0) | mode; + uint8_t lownib = ((value << 4) & 0xf0) | mode; // Send both nibbles - Wire.beginTransmission(_Addr); - write4bits(highnib | mode, true); - write4bits(lownib | mode, true); - Wire.endTransmission(); + byte buffer[] = {(byte)(highnib|En), highnib, (byte)(lownib|En), lownib}; + I2CManager.write(_Addr, buffer, sizeof(buffer)); } -// write 4 bits to the HD44780 interface. If inTransmission is false -// then the nibble will be sent in its own I2C transmission. -void LiquidCrystal_I2C::write4bits(uint8_t value, bool inTransmission) { - int _data = (int)value | _backlightval; - if (!inTransmission) Wire.beginTransmission(_Addr); +// write 4 bits to the HD44780 LCD controller. +void LiquidCrystal_I2C::write4bits(uint8_t value) { + uint8_t _data = value | _backlightval; // Enable must be set/reset for at least 450ns. This is well within the // I2C clock cycle time of 2.5us at 400kHz. Data is clocked in to the // HD44780 on the trailing edge of the Enable pin. - printIIC(_data | En); - printIIC(_data); - if (!inTransmission) Wire.endTransmission(); + byte buffer[] = {(byte)(_data|En), _data}; + I2CManager.write(_Addr, buffer, sizeof(buffer)); } -// write a byte to the PCF8574 I2C interface +// write a byte to the PCF8574 I2C interface. We don't need to set +// the enable pin for this. void LiquidCrystal_I2C::expanderWrite(uint8_t value) { - int _data = (int)value | _backlightval; - Wire.beginTransmission(_Addr); - printIIC(_data); - Wire.endTransmission(); + I2CManager.write(_Addr, 1, value | _backlightval); } \ No newline at end of file diff --git a/LiquidCrystal_I2C.h b/LiquidCrystal_I2C.h index 2d6919e..9316385 100644 --- a/LiquidCrystal_I2C.h +++ b/LiquidCrystal_I2C.h @@ -21,9 +21,7 @@ #ifndef LiquidCrystal_I2C_h #define LiquidCrystal_I2C_h -#include -#include "Print.h" -#include +#include // commands #define LCD_CLEARDISPLAY 0x01 @@ -82,25 +80,15 @@ public: void backlight(); void setCursor(uint8_t, uint8_t); -#if defined(ARDUINO) && ARDUINO >= 100 virtual size_t write(uint8_t); -#else - virtual void write(uint8_t); -#endif void command(uint8_t); void init(); - void oled_init(); - -////compatibility API function aliases -void setBacklight(uint8_t new_val); // alias for backlight() and nobacklight() -void printstr(const char[]); private: void init_priv(); void send(uint8_t, uint8_t); - void write4bits(uint8_t, bool inTransmission=false); + void write4bits(uint8_t); void expanderWrite(uint8_t); - void pulseEnable(uint8_t); uint8_t _Addr; uint8_t _displayfunction; uint8_t _displaycontrol; diff --git a/PWMServoDriver.cpp b/PWMServoDriver.cpp index 7db69d8..55d9ef5 100644 --- a/PWMServoDriver.cpp +++ b/PWMServoDriver.cpp @@ -23,7 +23,6 @@ * BSD license, all text above must be included in any redistribution */ #include -#include #include "PWMServoDriver.h" #include "DIAG.h" #include "I2CManager.h" @@ -60,7 +59,7 @@ bool PWMServoDriver::setup(int board) { uint8_t i2caddr=PCA9685_I2C_ADDRESS + board; // Test if device is available - byte error = I2CManager.exists(i2caddr); + byte error = I2CManager.checkAddress(i2caddr); if (error) { DIAG(F("I2C Servo device 0x%x Not Found %d"),i2caddr, error); failFlags|=1<> 8); - byte error=Wire.endTransmission(); + uint8_t buffer[] = {(uint8_t)(PCA9685_FIRST_SERVO + 4 * pin), // 4 registers per pin + 0, 0, (uint8_t)(value & 0xff), (uint8_t)(value >> 8)}; + if (value == 4095) buffer[2] = 0x10; // Full on + byte error=I2CManager.write(PCA9685_I2C_ADDRESS + board, buffer, sizeof(buffer)); if (error!=0) DIAG(F("SetServo error %d"),error); } } void PWMServoDriver::writeRegister(uint8_t i2caddr,uint8_t hardwareRegister, uint8_t d) { - Wire.beginTransmission(i2caddr); - Wire.write(hardwareRegister); - Wire.write(d); - Wire.endTransmission(); + I2CManager.write(i2caddr, 2, hardwareRegister, d); } diff --git a/SSD1306Ascii.cpp b/SSD1306Ascii.cpp index 85e406a..feb0f0d 100644 --- a/SSD1306Ascii.cpp +++ b/SSD1306Ascii.cpp @@ -16,27 +16,38 @@ * . */ #include "SSD1306Ascii.h" +#include "I2CManager.h" +#include "FSH.h" + + +// Maximum number of bytes we can send per transmission is 32. +const uint8_t FLASH SSD1306AsciiWire::blankPixels[32] = + {0x40, // First byte specifies data mode + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; //============================================================================== -// SSD1306Ascii Method Definitions - +// SSD1306AsciiWire Method Definitions //------------------------------------------------------------------------------ -void SSD1306Ascii::clear() { +void SSD1306AsciiWire::clear() { clear(0, displayWidth() - 1, 0, displayRows() - 1); } //------------------------------------------------------------------------------ -void SSD1306Ascii::clear(uint8_t c0, uint8_t c1, uint8_t r0, uint8_t r1) { +void SSD1306AsciiWire::clear(uint8_t columnStart, uint8_t columnEnd, + uint8_t rowStart, uint8_t rowEnd) { + const int maxBytes = sizeof(blankPixels); // max number of bytes sendable over Wire // Ensure only rows on display will be cleared. - if (r1 >= displayRows()) r1 = displayRows() - 1; - - for (uint8_t r = r0; r <= r1; r++) { - setCursor(c0, r); - for (uint8_t c = c0; c <= c1; c++) ssd1306WriteRamBuf(0); + if (rowEnd >= displayRows()) rowEnd = displayRows() - 1; + for (uint8_t r = rowStart; r <= rowEnd; r++) { + setCursor(columnStart, r); // Position at start of row to be erased + for (uint8_t c = columnStart; c <= columnEnd; c += maxBytes-1) { + uint8_t len = min((uint8_t)(columnEnd-c+1), maxBytes-1) + 1; + I2CManager.write_P(m_i2cAddr, blankPixels, len); // Write up to 31 blank columns + } } - setCursor(c0, r0); } //------------------------------------------------------------------------------ -void SSD1306Ascii::init(const DevType* dev) { +void SSD1306AsciiWire::begin(const DevType* dev, uint8_t i2cAddr) { + m_i2cAddr = i2cAddr; m_col = 0; m_row = 0; #ifdef __AVR__ @@ -48,125 +59,50 @@ void SSD1306Ascii::init(const DevType* dev) { m_displayWidth = readFontByte(&dev->lcdWidth); m_displayHeight = readFontByte(&dev->lcdHeight); m_colOffset = readFontByte(&dev->colOffset); - for (uint8_t i = 0; i < size; i++) { - ssd1306WriteCmd(readFontByte(table + i)); - } - clear(); + I2CManager.write_P(m_i2cAddr, table, size); } //------------------------------------------------------------------------------ -void SSD1306Ascii::setCol(uint8_t col) { - if (col < m_displayWidth) { - m_col = col; - col += m_colOffset; - ssd1306WriteCmd(SSD1306_SETLOWCOLUMN | (col & 0XF)); - ssd1306WriteCmd(SSD1306_SETHIGHCOLUMN | (col >> 4)); +void SSD1306AsciiWire::setContrast(uint8_t value) { + I2CManager.write(m_i2cAddr, 2, + 0x00, // Set to command mode + SSD1306_SETCONTRAST, value); +} +//------------------------------------------------------------------------------ +void SSD1306AsciiWire::setCursor(uint8_t col, uint8_t row) { + if (row < displayRows() && col < m_displayWidth) { + m_row = row; + m_col = col + m_colOffset; + I2CManager.write(m_i2cAddr, 4, + 0x00, // Set to command mode + SSD1306_SETLOWCOLUMN | (col & 0XF), + SSD1306_SETHIGHCOLUMN | (col >> 4), + SSD1306_SETSTARTPAGE | m_row); } } //------------------------------------------------------------------------------ -void SSD1306Ascii::setContrast(uint8_t value) { - ssd1306WriteCmd(SSD1306_SETCONTRAST); - ssd1306WriteCmd(value); -} -//------------------------------------------------------------------------------ -void SSD1306Ascii::setCursor(uint8_t col, uint8_t row) { - setCol(col); - setRow(row); -} -//------------------------------------------------------------------------------ -void SSD1306Ascii::setFont(const uint8_t* font) { +void SSD1306AsciiWire::setFont(const uint8_t* font) { m_font = font; m_fontFirstChar = readFontByte(m_font + FONT_FIRST_CHAR); m_fontCharCount = readFontByte(m_font + FONT_CHAR_COUNT); } //------------------------------------------------------------------------------ -void SSD1306Ascii::setRow(uint8_t row) { - if (row < displayRows()) { - m_row = row; - ssd1306WriteCmd(SSD1306_SETSTARTPAGE | m_row); - } -} -//------------------------------------------------------------------------------ -void SSD1306Ascii::ssd1306WriteRam(uint8_t c) { - if (m_col < m_displayWidth) { - writeDisplay(c, SSD1306_MODE_RAM); - m_col++; - } -} -//------------------------------------------------------------------------------ -void SSD1306Ascii::ssd1306WriteRamBuf(uint8_t c) { - if (m_col < m_displayWidth) { - writeDisplay(c, SSD1306_MODE_RAM_BUF); - m_col++; - } -} -//------------------------------------------------------------------------------ -size_t SSD1306Ascii::write(uint8_t ch) { - if (!m_font) { - return 0; - } +size_t SSD1306AsciiWire::write(uint8_t ch) { const uint8_t* base = m_font + FONT_WIDTH_TABLE; if (ch < m_fontFirstChar || ch >= (m_fontFirstChar + m_fontCharCount)) return 0; ch -= m_fontFirstChar; base += fontWidth * ch; - for (uint8_t c = 0; c < fontWidth; c++) { - uint8_t b = readFontByte(base + c); - ssd1306WriteRamBuf(b); - } - for (uint8_t i = 0; i < letterSpacing; i++) { - ssd1306WriteRamBuf(0); - } - flushDisplay(); + uint8_t buffer[1+fontWidth+letterSpacing]; + buffer[0] = 0x40; // set SSD1306 controller to data mode + uint8_t bufferPos = 1; + // Copy character pixel columns + for (uint8_t i = 0; i < fontWidth; i++) + buffer[bufferPos++] = readFontByte(base++); + // Add blank pixels between letters + for (uint8_t i = 0; i < letterSpacing; i++) + buffer[bufferPos++] = 0; + // Write the data to I2C display + I2CManager.write(m_i2cAddr, buffer, bufferPos); return 1; } - -//============================================================================= -// SSD1306AsciiWire method definitions - -#define m_oledWire Wire - -void SSD1306AsciiWire::begin(const DevType* dev, uint8_t i2cAddr) { -#if OPTIMIZE_I2C - m_nData = 0; -#endif // OPTIMIZE_I2C - m_i2cAddr = i2cAddr; - init(dev); -} - -//------------------------------------------------------------------------------ -void SSD1306AsciiWire::writeDisplay(uint8_t b, uint8_t mode) { -#if OPTIMIZE_I2C - if (m_nData > 16 || (m_nData && mode == SSD1306_MODE_CMD)) { - m_oledWire.endTransmission(); - m_nData = 0; - } - if (m_nData == 0) { - m_oledWire.beginTransmission(m_i2cAddr); - m_oledWire.write(mode == SSD1306_MODE_CMD ? 0X00 : 0X40); - } - m_oledWire.write(b); - if (mode == SSD1306_MODE_RAM_BUF) { - m_nData++; - } else { - m_oledWire.endTransmission(); - m_nData = 0; - } -#else // OPTIMIZE_I2C - m_oledWire.beginTransmission(m_i2cAddr); - m_oledWire.write(mode == SSD1306_MODE_CMD ? 0X00 : 0X40); - m_oledWire.write(b); - m_oledWire.endTransmission(); -#endif // OPTIMIZE_I2C -} - -//------------------------------------------------------------------------------ -void SSD1306AsciiWire::flushDisplay() { -#if OPTIMIZE_I2C - if (m_nData) { - m_oledWire.endTransmission(); - m_nData = 0; - } -#endif // OPTIMIZE_I2C -} -//------------------------------------------------------------------------------ diff --git a/SSD1306Ascii.h b/SSD1306Ascii.h index 2b4961f..e06b4cf 100644 --- a/SSD1306Ascii.h +++ b/SSD1306Ascii.h @@ -12,127 +12,41 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with the Arduino SSD1306Ascii Library. If not, see + * along with this software. If not, see * . */ -/** - * @file SSD1306AsciiWire.h - * @brief Class for I2C displays using Wire. - */ -#ifndef SSD1306AsciiWire_h -#define SSD1306AsciiWire_h -#include +#ifndef SSD1306Ascii_h +#define SSD1306Ascii_h #include "Arduino.h" -#include "LCDDisplay.h" #include "SSD1306font.h" #include "SSD1306init.h" -//------------------------------------------------------------------------------ -/** SSD1306Ascii version basis */ -#define SDD1306_ASCII_VERSION 1.3.0 -//------------------------------------------------------------------------------ -// Configuration options. - -/** Use larger faster I2C code. */ -#define OPTIMIZE_I2C 1 - -//------------------------------------------------------------------------------ -// Values for writeDisplay() mode parameter. -/** Write to Command register. */ -#define SSD1306_MODE_CMD 0 -/** Write one byte to display RAM. */ -#define SSD1306_MODE_RAM 1 -/** Write to display RAM with possible buffering. */ -#define SSD1306_MODE_RAM_BUF 2 -//------------------------------------------------------------------------------ - -/** - * @class SSD1306Ascii - * @brief SSD1306 base class - */ -class SSD1306Ascii : public Print { +class SSD1306AsciiWire : public Print { public: using Print::write; - SSD1306Ascii() {} - /** - * @brief Clear the display and set the cursor to (0, 0). - */ + SSD1306AsciiWire() {} + // Initialize the display controller. + void begin(const DevType* dev, uint8_t i2cAddr); + // Clear the display and set the cursor to (0, 0). void clear(); - /** - * @brief Clear a region of the display. - * - * @param[in] c0 Starting column. - * @param[in] c1 Ending column. - * @param[in] r0 Starting row; - * @param[in] r1 Ending row; - * @note The final cursor position will be (c0, r0). - */ + // Clear a region of the display. void clear(uint8_t c0, uint8_t c1, uint8_t r0, uint8_t r1); - /** - * @brief Clear a field of n fieldWidth() characters. - * - * @param[in] col Field start column. - * - * @param[in] row Field start row. - * - * @param[in] n Number of characters in the field. - * - */ - void clearField(uint8_t col, uint8_t row, uint8_t n); - /** - * @brief Clear the display to the end of the current line. - * @note The number of rows cleared will be determined by the height - * of the current font. - * @note The cursor will be returned to the original position. - */ - void clearToEOL(); - /** - * @return The current column in pixels. - */ + // The current column in pixels. inline uint8_t col() const { return m_col; } - /** - * @return The display hight in pixels. - */ + // The display hight in pixels. inline uint8_t displayHeight() const { return m_displayHeight; } - /** - * @brief Set display to normal or 180 degree remap mode. - * - * @param[in] mode true for normal mode, false for remap mode. - * - * @note Adafruit and many ebay displays use remap mode. - * Use normal mode to rotate these displays 180 degrees. - */ - void displayRemap(bool mode); - /** - * @return The display height in rows with eight pixels to a row. - */ + // The display height in rows with eight pixels to a row. inline uint8_t displayRows() const { return m_displayHeight / 8; } - /** - * @return The display width in pixels. - */ + // The display width in pixels. inline uint8_t displayWidth() const { return m_displayWidth; } - /** - * @brief Set the cursor position to (0, 0). - */ + // Set the cursor position to (0, 0). inline void home() { setCursor(0, 0); } - /** - * @brief Initialize the display controller. - * - * @param[in] dev A display initialization structure. - */ + // Initialize the display controller. void init(const DevType* dev); - /** - * @return the current row number with eight pixels to a row. - */ + // the current row number with eight pixels to a row. inline uint8_t row() const { return m_row; } - /** - * @brief Set the current column number. - * - * @param[in] col The desired column number in pixels. - */ - void setCol(uint8_t col); /** * @brief Set the display contrast. * @@ -152,34 +66,6 @@ class SSD1306Ascii : public Print { * @param[in] font Pointer to a font table. */ void setFont(const uint8_t* font); - /** - * @brief Set the current row number. - * - * @param[in] row the row number in eight pixel rows. - */ - void setRow(uint8_t row); - /** - * @brief Write a command byte to the display controller. - * - * @param[in] c The command byte. - * @note The byte will immediately be sent to the controller. - */ - inline void ssd1306WriteCmd(uint8_t c) { writeDisplay(c, SSD1306_MODE_CMD); } - /** - * @brief Write a byte to RAM in the display controller. - * - * @param[in] c The data byte. - * @note The byte will immediately be sent to the controller. - */ - void ssd1306WriteRam(uint8_t c); - /** - * @brief Write a byte to RAM in the display controller. - * - * @param[in] c The data byte. - * @note The byte may be buffered until a call to ssd1306WriteCmd - * or ssd1306WriteRam or flushDisplay. - */ - void ssd1306WriteRamBuf(uint8_t c); /** * @brief Display a character. * @@ -188,47 +74,24 @@ class SSD1306Ascii : public Print { */ size_t write(uint8_t c); - protected: - virtual void writeDisplay(uint8_t b, uint8_t mode) = 0; - virtual void flushDisplay() = 0; + private: uint8_t m_col; // Cursor column. uint8_t m_row; // Cursor RAM row. uint8_t m_displayWidth; // Display width. uint8_t m_displayHeight; // Display height. uint8_t m_colOffset; // Column offset RAM to SEG. - const uint8_t* m_font = nullptr; // Current font. + const uint8_t* m_font = NULL; // Current font. // Only fixed size 5x7 fonts in a 6x8 cell are supported. - const int fontWidth = 5; - const int fontHeight = 7; + const uint8_t fontWidth = 5; + const uint8_t fontHeight = 7; const uint8_t letterSpacing = 1; uint8_t m_fontFirstChar; uint8_t m_fontCharCount; -}; -/** - * @class SSD1306AsciiWire - * @brief Class for I2C displays using Wire. - */ -class SSD1306AsciiWire : public SSD1306Ascii { - public: - /** - * @brief Initialize the display controller. - * - * @param[in] dev A device initialization structure. - * @param[in] i2cAddr The I2C address of the display controller. - */ - void begin(const DevType* dev, uint8_t i2cAddr); - - protected: - void writeDisplay(uint8_t b, uint8_t mode); - void flushDisplay(); - - protected: uint8_t m_i2cAddr; -#if OPTIMIZE_I2C - uint8_t m_nData; -#endif // OPTIMIZE_I2C + + static const uint8_t blankPixels[]; }; -#endif // SSD1306AsciiWire_h +#endif // SSD1306Ascii_h diff --git a/SSD1306init.h b/SSD1306init.h index fb99deb..b861b81 100644 --- a/SSD1306init.h +++ b/SSD1306init.h @@ -118,6 +118,7 @@ struct DevType { /** Initialization commands for a 128x32 SSD1306 oled display. */ static const uint8_t MEM_TYPE Adafruit128x32init[] = { // Init sequence for Adafruit 128x32 OLED module + 0x00, // Set to command mode SSD1306_DISPLAYOFF, SSD1306_SETDISPLAYCLOCKDIV, 0x80, // the suggested ratio 0x80 SSD1306_SETMULTIPLEX, 0x1F, // ratio 32 @@ -148,6 +149,7 @@ static const DevType MEM_TYPE Adafruit128x32 = { /** Initialization commands for a 128x64 SSD1306 oled display. */ static const uint8_t MEM_TYPE Adafruit128x64init[] = { // Init sequence for Adafruit 128x64 OLED module + 0x00, // Set to command mode SSD1306_DISPLAYOFF, SSD1306_SETDISPLAYCLOCKDIV, 0x80, // the suggested ratio 0x80 SSD1306_SETMULTIPLEX, 0x3F, // ratio 64 @@ -177,6 +179,7 @@ static const DevType MEM_TYPE Adafruit128x64 = { // This section is based on https://github.com/stanleyhuangyc/MultiLCD /** Initialization commands for a 128x64 SH1106 oled display. */ static const uint8_t MEM_TYPE SH1106_128x64init[] = { + 0x00, // Set to command mode SSD1306_DISPLAYOFF, SSD1306_SETSTARTPAGE | 0X0, // set page address SSD1306_SETCONTRAST, 0x80, // 128