diff --git a/src/SFE_MicroOLED.cpp b/src/SFE_MicroOLED.cpp index c688070..bb88dc3 100644 --- a/src/SFE_MicroOLED.cpp +++ b/src/SFE_MicroOLED.cpp @@ -88,7 +88,7 @@ along with this program. If not, see . // Add the font name as declared in the header file. // Exclude as many as possible to conserve FLASH memory. -const unsigned char *MicroOLED::fontsPointer[] = { +static const unsigned char *fontsPointer[] = { #if INCLUDE_FONT_5x7 font5x7, #else @@ -180,11 +180,10 @@ static uint8_t screenmemory[] = { Setup the MicroOLED class, assuming that the I2C address will be defined later. */ -MicroOLED::MicroOLED(uint8_t rst) +MicroOLED_I2C::MicroOLED_I2C(uint8_t rst) : MicroOLEDBase() { // Assign each of the parameters to a private class variable. rstPin = rst; - moled_interface = MOLED_MODE_I2C; // Set interface to I2C moled_i2c_address = I2C_ADDRESS_UNDEFINED; // Flag that the I2C address is undefined } @@ -193,13 +192,12 @@ MicroOLED::MicroOLED(uint8_t rst) Setup the MicroOLED class, configure the display to be controlled via a SPI interface. */ -MicroOLED::MicroOLED(uint8_t rst, uint8_t dc, uint8_t cs) +MicroOLED_SPI::MicroOLED_SPI(uint8_t rst, uint8_t dc, uint8_t cs) : MicroOLEDBase() { // Assign each of the parameters to a private class variable. rstPin = rst; dcPin = dc; csPin = cs; - moled_interface = MOLED_MODE_SPI; // Set interface mode to SPI //_spiPort will be initialized by spiSetup } @@ -208,10 +206,9 @@ MicroOLED::MicroOLED(uint8_t rst, uint8_t dc, uint8_t cs) Setup the MicroOLED class, configure the display to be controlled via a I2C interface. */ -MicroOLED::MicroOLED(uint8_t rst, uint8_t dc) +MicroOLED_I2C::MicroOLED_I2C(uint8_t rst, uint8_t dc) : MicroOLEDBase() { rstPin = rst; // Assign reset pin to private class variable - moled_interface = MOLED_MODE_I2C; // Set interface to I2C // Set the I2C Address based on whether DC is high (1) or low (0). // The pin is pulled low by default, so if it's not explicitly set to // 1, just default to 0. @@ -227,11 +224,21 @@ MicroOLED::MicroOLED(uint8_t rst, uint8_t dc) Setup the MicroOLED class, configure the display to be controlled via a parallel interface. */ -MicroOLED::MicroOLED(uint8_t rst, uint8_t dc, uint8_t cs, uint8_t wr, uint8_t rd, - uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, - uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7) +MicroOLED_Parallel::MicroOLED_Parallel(uint8_t rst, + uint8_t dc, + uint8_t cs, + uint8_t wr, + uint8_t rd, + uint8_t d0, + uint8_t d1, + uint8_t d2, + uint8_t d3, + uint8_t d4, + uint8_t d5, + uint8_t d6, + uint8_t d7) : + MicroOLEDBase() { - moled_interface = MOLED_MODE_PARALLEL; // Set to parallel mode // Assign pin parameters to private class variables. rstPin = rst; dcPin = dc; @@ -252,65 +259,81 @@ MicroOLED::MicroOLED(uint8_t rst, uint8_t dc, uint8_t cs, uint8_t wr, uint8_t rd Setup for the chosen interface then send initialisation commands to the SSD1306 controller inside the OLED. */ -boolean MicroOLED::begin() -{ - // Set up the selected interface: - if (moled_interface == MOLED_MODE_SPI) - spiSetup(); - else if (moled_interface == MOLED_MODE_I2C) - i2cSetup(); - else if (moled_interface == MOLED_MODE_PARALLEL) - parallelSetup(); - else //if (moled_interface == MOLED_MODE_UNDEFINED) - return (false); - - // Trap if the user instantiated with MicroOLED oled(PIN_RESET) and then called - // .begin instead of .begin(uint8_t deviceAddress, TwoWire &wirePort) - if ((moled_interface == MOLED_MODE_I2C) && (moled_i2c_address == I2C_ADDRESS_UNDEFINED)) - { - if (_printDebug == true) - { - _debugPort->println(F("begin: error! deviceAddress is I2C_ADDRESS_UNDEFINED!")); - _debugPort->println(F("begin: Did you forget to call .begin(uint8_t deviceAddress, TwoWire &wirePort)?")); - } - return (false); - } +boolean MicroOLED_SPI::begin() +{ + spiSetup(); - beginCommon(); - return (true); + beginCommon(); + return (true); } /** \brief Initialisation of MicroOLED Library. - Setup IO pins for the SPI interface then send initialisation commands to the SSD1306 controller inside the OLED. + Setup IO pins for the SPI interface then send initialisation commands to the SSD1306 + controller inside the OLED. */ -boolean MicroOLED::begin(SPIClass &spiPort) +boolean MicroOLED_SPI::begin(SPIClass & spiPort) { - // Set up the selected interface: - spiSetup(spiPort); + // Set up the selected interface: + spiSetup(spiPort); - beginCommon(); - return (true); + beginCommon(); + return (true); +} + +boolean MicroOLED_I2C::begin() +{ + i2cSetup(); + + // Trap if the user instantiated with MicroOLED oled(PIN_RESET) and then called + // .begin instead of .begin(uint8_t deviceAddress, TwoWire &wirePort) + if (moled_i2c_address == I2C_ADDRESS_UNDEFINED) + { + if (_printDebug == true) + { + _debugPort->println( + F("begin: error! deviceAddress is I2C_ADDRESS_UNDEFINED!")); + _debugPort->println( + F("begin: Did you forget to call .begin(uint8_t deviceAddress, TwoWire " + "&wirePort)?")); + } + return (false); + } + + beginCommon(); + return (true); } /** \brief Initialisation of MicroOLED Library. - Setup IO pins for the I2C interface then send initialisation commands to the SSD1306 controller inside the OLED. + Setup IO pins for the I2C interface then send initialisation commands to the SSD1306 + controller inside the OLED. */ -boolean MicroOLED::begin(uint8_t deviceAddress, TwoWire &wirePort) +boolean MicroOLED_I2C::begin(uint8_t deviceAddress, TwoWire & wirePort) +{ + // Set up the selected interface: + i2cSetup(deviceAddress, wirePort); + + beginCommon(); + return (true); +} + +boolean MicroOLED_Parallel::begin() { - // Set up the selected interface: - i2cSetup(deviceAddress, wirePort); + parallelSetup(); - beginCommon(); - return (true); + beginCommon(); + return (true); } + + /** \brief Initialisation of MicroOLED Library - common to all begin methods. PRIVATE. Setup IO pins for the chosen interface then send initialisation commands to the SSD1306 controller inside the OLED. */ -void MicroOLED::beginCommon() +template +void MicroOLEDBase::beginCommon() { // default 5x7 font setFontType(0); @@ -367,7 +390,8 @@ void MicroOLED::beginCommon() //Calling this function with nothing sets the debug port to Serial //You can also call it with other streams like Serial1, SerialUSB, etc. -void MicroOLED::enableDebugging(Stream &debugPort) +template +void MicroOLEDBase::enableDebugging(Stream &debugPort) { _debugPort = &debugPort; _printDebug = true; @@ -380,26 +404,11 @@ void MicroOLED::enableDebugging(Stream &debugPort) to send the data. For I2C and Parallel we use the write functions defined in hardware.cpp to send the data. */ -void MicroOLED::command(uint8_t c) +template +void MicroOLEDBase::command(uint8_t c) { - - if (moled_interface == MOLED_MODE_SPI) - { - digitalWrite(dcPin, LOW); - ; // DC pin LOW for a command - spiTransfer(c); // Transfer the command byte - } - else if (moled_interface == MOLED_MODE_I2C) - { - // Write to our address, make sure it knows we're sending a - // command: - i2cWrite(moled_i2c_address, I2C_COMMAND, c); - } - else if (moled_interface == MOLED_MODE_PARALLEL) - { - // Write the byte to our parallel interface. Set DC LOW. - parallelWrite(c, LOW); - } + /* Delegate to derived class, which knows how to send a command. */ + static_cast(this)->command(c); } /** \brief Send the display a data byte @@ -409,33 +418,19 @@ void MicroOLED::command(uint8_t c) to send the data. For I2C and Parallel we use the write functions defined in hardware.cpp to send the data. */ -void MicroOLED::data(uint8_t c) +template +void MicroOLEDBase::data(uint8_t c) { - - if (moled_interface == MOLED_MODE_SPI) - { - digitalWrite(dcPin, HIGH); // DC HIGH for a data byte - - spiTransfer(c); // Transfer the data byte - } - else if (moled_interface == MOLED_MODE_I2C) - { - // Write to our address, make sure it knows we're sending a - // data byte: - i2cWrite(moled_i2c_address, I2C_DATA, c); - } - else if (moled_interface == MOLED_MODE_PARALLEL) - { - // Write the byte to our parallel interface. Set DC HIGH. - parallelWrite(c, HIGH); - } + /* Delegate to derived class, which knows how to send data. */ + static_cast(this)->data(c); } /** \brief Set SSD1306 page address. Send page address command and address to the SSD1306 OLED controller. */ -void MicroOLED::setPageAddress(uint8_t add) +template +void MicroOLEDBase::setPageAddress(uint8_t add) { add = 0xb0 | add; command(add); @@ -446,7 +441,8 @@ void MicroOLED::setPageAddress(uint8_t add) Send column address command and address to the SSD1306 OLED controller. */ -void MicroOLED::setColumnAddress(uint8_t add) +template +void MicroOLEDBase::setColumnAddress(uint8_t add) { command((0x10 | (add >> 4)) + 0x02); command((0x0f & add)); @@ -457,7 +453,8 @@ void MicroOLED::setColumnAddress(uint8_t add) To clear GDRAM inside the LCD controller, pass in the variable mode = ALL and to clear screen page buffer pass in the variable mode = PAGE. */ -void MicroOLED::clear(uint8_t mode) +template +void MicroOLEDBase::clear(uint8_t mode) { // uint8_t page=6, col=0x40; if (mode == ALL) @@ -466,19 +463,7 @@ void MicroOLED::clear(uint8_t mode) { setPageAddress(i); setColumnAddress(0); - if (moled_interface == MOLED_MODE_I2C) - { - uint8_t zeros[0x80]; - memset(zeros, 0, 0x80); - i2cWriteMultiple(moled_i2c_address, (uint8_t *)&zeros, 0x80); - } - else - { - for (int j = 0; j < 0x80; j++) - { - data(0); - } - } + static_cast(this)->constantData(0, 0x80); } } else @@ -492,7 +477,8 @@ void MicroOLED::clear(uint8_t mode) To clear GDRAM inside the LCD controller, pass in the variable mode = ALL with c character and to clear screen page buffer, pass in the variable mode = PAGE with c character. */ -void MicroOLED::clear(uint8_t mode, uint8_t c) +template +void MicroOLEDBase::clear(uint8_t mode, uint8_t c) { //uint8_t page=6, col=0x40; if (mode == ALL) @@ -501,19 +487,7 @@ void MicroOLED::clear(uint8_t mode, uint8_t c) { setPageAddress(i); setColumnAddress(0); - if (moled_interface == MOLED_MODE_I2C) - { - uint8_t zeros[0x80]; - memset(zeros, c, 0x80); - i2cWriteMultiple(moled_i2c_address, (uint8_t *)&zeros, 0x80); - } - else - { - for (int j = 0; j < 0x80; j++) - { - data(c); - } - } + static_cast(this)->constantData(c, 0x80); } } else @@ -527,7 +501,8 @@ void MicroOLED::clear(uint8_t mode, uint8_t c) The WHITE color of the display will turn to BLACK and the BLACK will turn to WHITE. */ -void MicroOLED::invert(boolean inv) +template +void MicroOLEDBase::invert(boolean inv) { if (inv) command(INVERTDISPLAY); @@ -539,7 +514,8 @@ void MicroOLED::invert(boolean inv) OLED contract value from 0 to 255. Note: Contrast level is not very obvious. */ -void MicroOLED::contrast(uint8_t contrast) +template +void MicroOLEDBase::contrast(uint8_t contrast) { command(SETCONTRAST); // 0x81 command(contrast); @@ -549,7 +525,8 @@ void MicroOLED::contrast(uint8_t contrast) Bulk move the screen buffer to the SSD1306 controller's memory so that images/graphics drawn on the screen buffer will be displayed on the OLED. */ -void MicroOLED::display(void) +template +void MicroOLEDBase::display(void) { uint8_t i, j; @@ -557,17 +534,7 @@ void MicroOLED::display(void) { setPageAddress(i); setColumnAddress(0); - if (moled_interface == MOLED_MODE_I2C) - { - i2cWriteMultiple(moled_i2c_address, (uint8_t *)&screenmemory[i * 0x40], 0x40); - } - else - { - for (j = 0; j < 0x40; j++) - { - data(screenmemory[i * 0x40 + j]); - } - } + static_cast(this)->multipleData(screenmemory + i*0x40, 0x40); } } @@ -575,7 +542,8 @@ void MicroOLED::display(void) Arduino's print overridden so that we can use uView.print(). */ -size_t MicroOLED::write(uint8_t c) +template +size_t MicroOLEDBase::write(uint8_t c) { if (c == '\n') { @@ -604,7 +572,8 @@ size_t MicroOLED::write(uint8_t c) MicroOLED's cursor position to x,y. */ -void MicroOLED::setCursor(uint8_t x, uint8_t y) +template +void MicroOLEDBase::setCursor(uint8_t x, uint8_t y) { cursorX = x; cursorY = y; @@ -614,7 +583,8 @@ void MicroOLED::setCursor(uint8_t x, uint8_t y) Draw pixel using the current fore color and current draw mode in the screen buffer's x,y position. */ -void MicroOLED::pixel(uint8_t x, uint8_t y) +template +void MicroOLEDBase::pixel(uint8_t x, uint8_t y) { pixel(x, y, foreColor, drawMode); } @@ -623,7 +593,8 @@ void MicroOLED::pixel(uint8_t x, uint8_t y) Draw color pixel in the screen buffer's x,y position with NORM or XOR draw mode. */ -void MicroOLED::pixel(uint8_t x, uint8_t y, uint8_t color, uint8_t mode) +template +void MicroOLEDBase::pixel(uint8_t x, uint8_t y, uint8_t color, uint8_t mode) { if ((x < 0) || (x >= LCDWIDTH) || (y < 0) || (y >= LCDHEIGHT)) return; @@ -646,7 +617,8 @@ void MicroOLED::pixel(uint8_t x, uint8_t y, uint8_t color, uint8_t mode) Draw line using current fore color and current draw mode from x0,y0 to x1,y1 of the screen buffer. */ -void MicroOLED::line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) +template +void MicroOLEDBase::line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) { line(x0, y0, x1, y1, foreColor, drawMode); } @@ -655,7 +627,8 @@ void MicroOLED::line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1) Draw line using color and mode from x0,y0 to x1,y1 of the screen buffer. */ -void MicroOLED::line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t color, uint8_t mode) +template +void MicroOLEDBase::line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t color, uint8_t mode) { if (_printDebug == true) { @@ -783,7 +756,8 @@ void MicroOLED::line(uint8_t x0, uint8_t y0, uint8_t x1, uint8_t y1, uint8_t col Draw horizontal line using current fore color and current draw mode from x,y to x+width,y of the screen buffer. */ -void MicroOLED::lineH(uint8_t x, uint8_t y, uint8_t width) +template +void MicroOLEDBase::lineH(uint8_t x, uint8_t y, uint8_t width) { line(x, y, x + width, y, foreColor, drawMode); } @@ -792,7 +766,8 @@ void MicroOLED::lineH(uint8_t x, uint8_t y, uint8_t width) Draw horizontal line using color and mode from x,y to x+width,y of the screen buffer. */ -void MicroOLED::lineH(uint8_t x, uint8_t y, uint8_t width, uint8_t color, uint8_t mode) +template +void MicroOLEDBase::lineH(uint8_t x, uint8_t y, uint8_t width, uint8_t color, uint8_t mode) { line(x, y, x + width, y, color, mode); } @@ -801,7 +776,8 @@ void MicroOLED::lineH(uint8_t x, uint8_t y, uint8_t width, uint8_t color, uint8_ Draw vertical line using current fore color and current draw mode from x,y to x,y+height of the screen buffer. */ -void MicroOLED::lineV(uint8_t x, uint8_t y, uint8_t height) +template +void MicroOLEDBase::lineV(uint8_t x, uint8_t y, uint8_t height) { line(x, y, x, y + height, foreColor, drawMode); } @@ -810,7 +786,8 @@ void MicroOLED::lineV(uint8_t x, uint8_t y, uint8_t height) Draw vertical line using color and mode from x,y to x,y+height of the screen buffer. */ -void MicroOLED::lineV(uint8_t x, uint8_t y, uint8_t height, uint8_t color, uint8_t mode) +template +void MicroOLEDBase::lineV(uint8_t x, uint8_t y, uint8_t height, uint8_t color, uint8_t mode) { line(x, y, x, y + height, color, mode); } @@ -819,7 +796,8 @@ void MicroOLED::lineV(uint8_t x, uint8_t y, uint8_t height, uint8_t color, uint8 Draw rectangle using current fore color and current draw mode from x,y to x+width,y+height of the screen buffer. */ -void MicroOLED::rect(uint8_t x, uint8_t y, uint8_t width, uint8_t height) +template +void MicroOLEDBase::rect(uint8_t x, uint8_t y, uint8_t width, uint8_t height) { rect(x, y, width, height, foreColor, drawMode); } @@ -828,7 +806,8 @@ void MicroOLED::rect(uint8_t x, uint8_t y, uint8_t width, uint8_t height) Draw rectangle using color and mode from x,y to x+width,y+height of the screen buffer. */ -void MicroOLED::rect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) +template +void MicroOLEDBase::rect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) { uint8_t tempHeight; @@ -850,7 +829,8 @@ void MicroOLED::rect(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_ Draw filled rectangle using current fore color and current draw mode from x,y to x+width,y+height of the screen buffer. */ -void MicroOLED::rectFill(uint8_t x, uint8_t y, uint8_t width, uint8_t height) +template +void MicroOLEDBase::rectFill(uint8_t x, uint8_t y, uint8_t width, uint8_t height) { rectFill(x, y, width, height, foreColor, drawMode); } @@ -859,7 +839,8 @@ void MicroOLED::rectFill(uint8_t x, uint8_t y, uint8_t width, uint8_t height) Draw filled rectangle using color and mode from x,y to x+width,y+height of the screen buffer. */ -void MicroOLED::rectFill(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) +template +void MicroOLEDBase::rectFill(uint8_t x, uint8_t y, uint8_t width, uint8_t height, uint8_t color, uint8_t mode) { // TODO - need to optimise the memory map draw so that this function will not call pixel one by one for (int i = x; i < x + width; i++) @@ -872,7 +853,8 @@ void MicroOLED::rectFill(uint8_t x, uint8_t y, uint8_t width, uint8_t height, ui Draw circle with radius using current fore color and current draw mode at x,y of the screen buffer. */ -void MicroOLED::circle(uint8_t x0, uint8_t y0, uint8_t radius) +template +void MicroOLEDBase::circle(uint8_t x0, uint8_t y0, uint8_t radius) { circle(x0, y0, radius, foreColor, drawMode); } @@ -881,7 +863,8 @@ void MicroOLED::circle(uint8_t x0, uint8_t y0, uint8_t radius) Draw circle with radius using color and mode at x,y of the screen buffer. */ -void MicroOLED::circle(uint8_t x0, uint8_t y0, uint8_t radius, uint8_t color, uint8_t mode) +template +void MicroOLEDBase::circle(uint8_t x0, uint8_t y0, uint8_t radius, uint8_t color, uint8_t mode) { //TODO - find a way to check for no overlapping of pixels so that XOR draw mode will work perfectly int8_t f = 1 - radius; @@ -923,7 +906,8 @@ void MicroOLED::circle(uint8_t x0, uint8_t y0, uint8_t radius, uint8_t color, ui Draw filled circle with radius using current fore color and current draw mode at x,y of the screen buffer. */ -void MicroOLED::circleFill(uint8_t x0, uint8_t y0, uint8_t radius) +template +void MicroOLEDBase::circleFill(uint8_t x0, uint8_t y0, uint8_t radius) { circleFill(x0, y0, radius, foreColor, drawMode); } @@ -932,7 +916,8 @@ void MicroOLED::circleFill(uint8_t x0, uint8_t y0, uint8_t radius) Draw filled circle with radius using color and mode at x,y of the screen buffer. */ -void MicroOLED::circleFill(uint8_t x0, uint8_t y0, uint8_t radius, uint8_t color, uint8_t mode) +template +void MicroOLEDBase::circleFill(uint8_t x0, uint8_t y0, uint8_t radius, uint8_t color, uint8_t mode) { // TODO - - find a way to check for no overlapping of pixels so that XOR draw mode will work perfectly int8_t f = 1 - radius; @@ -979,7 +964,8 @@ void MicroOLED::circleFill(uint8_t x0, uint8_t y0, uint8_t radius, uint8_t color The height of the LCD return as byte. */ -uint8_t MicroOLED::getLCDHeight(void) +template +uint8_t MicroOLEDBase::getLCDHeight(void) { return LCDHEIGHT; } @@ -988,7 +974,8 @@ uint8_t MicroOLED::getLCDHeight(void) The width of the LCD return as byte. */ -uint8_t MicroOLED::getLCDWidth(void) +template +uint8_t MicroOLEDBase::getLCDWidth(void) { return LCDWIDTH; } @@ -997,7 +984,8 @@ uint8_t MicroOLED::getLCDWidth(void) The cucrrent font's width return as byte. */ -uint8_t MicroOLED::getFontWidth(void) +template +uint8_t MicroOLEDBase::getFontWidth(void) { return fontWidth; } @@ -1006,7 +994,8 @@ uint8_t MicroOLED::getFontWidth(void) The current font's height return as byte. */ -uint8_t MicroOLED::getFontHeight(void) +template +uint8_t MicroOLEDBase::getFontHeight(void) { return fontHeight; } @@ -1015,7 +1004,8 @@ uint8_t MicroOLED::getFontHeight(void) Return the starting ASCII character of the currnet font, not all fonts start with ASCII character 0. Custom fonts can start from any ASCII character. */ -uint8_t MicroOLED::getFontStartChar(void) +template +uint8_t MicroOLEDBase::getFontStartChar(void) { return fontStartChar; } @@ -1024,7 +1014,8 @@ uint8_t MicroOLED::getFontStartChar(void) Return the total characters of the current font. */ -uint8_t MicroOLED::getFontTotalChar(void) +template +uint8_t MicroOLEDBase::getFontTotalChar(void) { return fontTotalChar; } @@ -1033,7 +1024,8 @@ uint8_t MicroOLED::getFontTotalChar(void) Return the total number of fonts loaded into the MicroOLED's flash memory. */ -uint8_t MicroOLED::getTotalFonts(void) +template +uint8_t MicroOLEDBase::getTotalFonts(void) { uint8_t totalFonts = 0; for (uint8_t thisFont = 0; thisFont < MAXFONTS; thisFont++) @@ -1048,7 +1040,8 @@ uint8_t MicroOLED::getTotalFonts(void) Return the font type number of the current font. */ -uint8_t MicroOLED::getFontType(void) +template +uint8_t MicroOLEDBase::getFontType(void) { return fontType; } @@ -1057,7 +1050,8 @@ uint8_t MicroOLED::getFontType(void) Set the current font type number, ie changing to different fonts base on the type provided. */ -uint8_t MicroOLED::setFontType(uint8_t type) +template +uint8_t MicroOLEDBase::setFontType(uint8_t type) { if ((type >= MAXFONTS) || (fontsPointer[type] == NULL)) return false; @@ -1075,7 +1069,8 @@ uint8_t MicroOLED::setFontType(uint8_t type) Set the current draw's color. Only WHITE and BLACK available. */ -void MicroOLED::setColor(uint8_t color) +template +void MicroOLEDBase::setColor(uint8_t color) { foreColor = color; } @@ -1084,7 +1079,8 @@ void MicroOLED::setColor(uint8_t color) Set current draw mode with NORM or XOR. */ -void MicroOLED::setDrawMode(uint8_t mode) +template +void MicroOLEDBase::setDrawMode(uint8_t mode) { drawMode = mode; } @@ -1093,7 +1089,8 @@ void MicroOLED::setDrawMode(uint8_t mode) Draw character c using current color and current draw mode at x,y. */ -void MicroOLED::drawChar(uint8_t x, uint8_t y, uint8_t c) +template +void MicroOLEDBase::drawChar(uint8_t x, uint8_t y, uint8_t c) { drawChar(x, y, c, foreColor, drawMode); } @@ -1102,7 +1099,8 @@ void MicroOLED::drawChar(uint8_t x, uint8_t y, uint8_t c) Draw character c using color and draw mode at x,y. */ -void MicroOLED::drawChar(uint8_t x, uint8_t y, uint8_t c, uint8_t color, uint8_t mode) +template +void MicroOLEDBase::drawChar(uint8_t x, uint8_t y, uint8_t c, uint8_t color, uint8_t mode) { // TODO - New routine to take font of any height, at the moment limited to font height in multiple of 8 pixels @@ -1180,7 +1178,8 @@ void MicroOLED::drawChar(uint8_t x, uint8_t y, uint8_t c, uint8_t color, uint8_t Stop the scrolling of graphics on the OLED. */ -void MicroOLED::scrollStop(void) +template +void MicroOLEDBase::scrollStop(void) { command(DEACTIVATESCROLL); } @@ -1189,7 +1188,8 @@ void MicroOLED::scrollStop(void) Set row start to row stop on the OLED to scroll right. */ -void MicroOLED::scrollRight(uint8_t start, uint8_t stop, uint8_t scrollInterval) +template +void MicroOLEDBase::scrollRight(uint8_t start, uint8_t stop, uint8_t scrollInterval) { if (stop < start) // stop must be larger or equal to start return; @@ -1208,7 +1208,8 @@ void MicroOLED::scrollRight(uint8_t start, uint8_t stop, uint8_t scrollInterval) Set row start to row stop on the OLED to scroll vert right. */ -void MicroOLED::scrollVertRight(uint8_t start, uint8_t stop, uint8_t scrollInterval) +template +void MicroOLEDBase::scrollVertRight(uint8_t start, uint8_t stop, uint8_t scrollInterval) { if (stop < start) // stop must be larger or equal to start return; @@ -1226,7 +1227,8 @@ void MicroOLED::scrollVertRight(uint8_t start, uint8_t stop, uint8_t scrollInter Set row start to row stop on the OLED to scroll left. */ -void MicroOLED::scrollLeft(uint8_t start, uint8_t stop, uint8_t scrollInterval) +template +void MicroOLEDBase::scrollLeft(uint8_t start, uint8_t stop, uint8_t scrollInterval) { if (stop < start) // stop must be larger or equal to start return; @@ -1245,7 +1247,8 @@ void MicroOLED::scrollLeft(uint8_t start, uint8_t stop, uint8_t scrollInterval) Set row start to row stop on the OLED to scroll vert left. */ -void MicroOLED::scrollVertLeft(uint8_t start, uint8_t stop, uint8_t scrollInterval) +template +void MicroOLEDBase::scrollVertLeft(uint8_t start, uint8_t stop, uint8_t scrollInterval) { if (stop < start) // stop must be larger or equal to start return; @@ -1263,7 +1266,8 @@ void MicroOLED::scrollVertLeft(uint8_t start, uint8_t stop, uint8_t scrollInterv Flip the graphics on the OLED vertically. */ -void MicroOLED::flipVertical(boolean flip) +template +void MicroOLEDBase::flipVertical(boolean flip) { if (flip) { @@ -1279,7 +1283,8 @@ void MicroOLED::flipVertical(boolean flip) Flip the graphics on the OLED horizontally. */ -void MicroOLED::flipHorizontal(boolean flip) +template +void MicroOLEDBase::flipHorizontal(boolean flip) { if (flip) { @@ -1294,7 +1299,8 @@ void MicroOLED::flipHorizontal(boolean flip) /* Return a pointer to the start of the RAM screen buffer for direct access. */ -uint8_t *MicroOLED::getScreenBuffer(void) +template +uint8_t *MicroOLEDBase::getScreenBuffer(void) { return screenmemory; } @@ -1303,7 +1309,8 @@ uint8_t *MicroOLED::getScreenBuffer(void) Draw Bitmap image on screen. The array for the bitmap can be stored in the Arduino file, so user don't have to mess with the library files. To use, create uint8_t array that is 64x48 pixels (384 bytes). Then call .drawBitmap and pass it the array. */ -void MicroOLED::drawBitmap(uint8_t *bitArray) +template +void MicroOLEDBase::drawBitmap(uint8_t *bitArray) { for (int i = 0; i < (LCDWIDTH * LCDHEIGHT / 8); i++) screenmemory[i] = bitArray[i]; @@ -1313,7 +1320,8 @@ void MicroOLED::drawBitmap(uint8_t *bitArray) //Use http://en.radzio.dxp.pl/bitmap_converter/ to generate output //Make sure the bitmap is n*8 pixels tall (pad white pixels to lower area as needed) //Otherwise the bitmap bitmap_converter will compress some of the bytes together -void MicroOLED::drawIcon(uint8_t offsetX, uint8_t offsetY, uint8_t iconWidth, uint8_t iconHeight, uint8_t *bitArray, uint8_t arraySizeInBytes, bool overwrite) +template +void MicroOLEDBase::drawIcon(uint8_t offsetX, uint8_t offsetY, uint8_t iconWidth, uint8_t iconHeight, uint8_t *bitArray, uint8_t arraySizeInBytes, bool overwrite) { uint8_t columnNumber = offsetX; uint8_t rowNumber = offsetY / 8; @@ -1366,18 +1374,24 @@ void MicroOLED::drawIcon(uint8_t offsetX, uint8_t offsetY, uint8_t iconWidth, ui //Most platforms use 32 bytes (the default) but this allows users to increase the transaction //size if the platform supports it //Note: If the transaction size is set larger than the platforms buffer size, bad things will happen. -void MicroOLED::setI2CTransactionSize(uint8_t transactionSize) +void MicroOLED_I2C::setI2CTransactionSize(uint8_t transactionSize) { i2cTransactionSize = transactionSize; } -uint8_t MicroOLED::getI2CTransactionSize(void) +uint8_t MicroOLED_I2C::getI2CTransactionSize(void) { return (i2cTransactionSize); } -void MicroOLED::swapOLED(uint8_t *x, uint8_t *y) +template +void MicroOLEDBase::swapOLED(uint8_t *x, uint8_t *y) { uint8_t t = *x; *x = *y; *y = t; } + +/* Explicitly instantiate the base classes. */ +template class MicroOLEDBase; +template class MicroOLEDBase; +template class MicroOLEDBase; diff --git a/src/SFE_MicroOLED.h b/src/SFE_MicroOLED.h index 534ec81..23fcd1e 100644 --- a/src/SFE_MicroOLED.h +++ b/src/SFE_MicroOLED.h @@ -140,30 +140,16 @@ typedef enum CMD CMD_SETDRAWMODE //18 } commCommand_t; -typedef enum COMM_MODE -{ - MOLED_MODE_SPI, - MOLED_MODE_I2C, - MOLED_MODE_PARALLEL, - MOLED_MODE_UNDEFINED -} micro_oled_mode; - -class MicroOLED : public Print +/* Base class containing the common MicroOLED drawing code. The + * comm-specific code is in the derived classes, which are supplied + * through a template parameter (the Curiously Recursive Template + * Pattern). This avoids virtual dispatches. + */ +template +class MicroOLEDBase : public Print { public: - // Constructor(s) - MicroOLED(uint8_t rst); // I2C - leaving the address currently undefined - MicroOLED(uint8_t rst, uint8_t dc); // I2C - MicroOLED(uint8_t rst, uint8_t dc, uint8_t cs); // SPI - MicroOLED(uint8_t rst, uint8_t dc, uint8_t cs, uint8_t wr, uint8_t rd, - uint8_t d0, uint8_t d1, uint8_t d2, uint8_t d3, - uint8_t d4, uint8_t d5, uint8_t d6, uint8_t d7); // Parallel - - boolean begin(void); // Needed for backward-compatibility - boolean begin(uint8_t deviceAddress, TwoWire &wirePort); // User-defined I2C address and TwoWire - boolean begin(SPIClass &spiPort); // User-defined SPIClass - - virtual size_t write(uint8_t); // Virtual - for I2C _or_ SPI + size_t write(uint8_t) override; void enableDebugging(Stream &debugPort = Serial); //Turn on debug printing. If user doesn't specify then Serial will be used. @@ -225,25 +211,14 @@ class MicroOLED : public Print void flipVertical(boolean flip); void flipHorizontal(boolean flip); - //Control the size of the internal I2C transaction amount - void setI2CTransactionSize(uint8_t bufferSize); - uint8_t getI2CTransactionSize(void); - - //Set the max number of bytes set in a given I2C transaction - uint8_t i2cTransactionSize = 32; //Default to ATmega328 limit - -private: +protected: uint8_t csPin, dcPin, rstPin; - uint8_t wrPin, rdPin, dPins[8]; volatile uint8_t *wrport, *wrreg, *rdport, *rdreg; uint8_t wrpinmask, rdpinmask; - micro_oled_mode moled_interface = MOLED_MODE_UNDEFINED; - byte moled_i2c_address = I2C_ADDRESS_UNDEFINED; volatile uint8_t *ssport, *dcport, *ssreg, *dcreg; // use volatile because these are fixed location port address uint8_t mosipinmask, sckpinmask, sspinmask, dcpinmask; uint8_t foreColor, drawMode, fontWidth, fontHeight, fontType, fontStartChar, fontTotalChar, cursorX, cursorY; uint16_t fontMapWidth; - static const unsigned char *fontsPointer[]; void swapOLED(uint8_t *x, uint8_t *y); //Debug @@ -253,16 +228,95 @@ class MicroOLED : public Print void beginCommon(); // Functionality common to all begin methods // Communication - void spiTransfer(byte data); - void spiSetup(SPIClass &spiPort = SPI); - void i2cSetup(uint8_t deviceAddress = I2C_ADDRESS_UNDEFINED, TwoWire &wirePort = Wire); - void i2cWrite(byte address, byte control, byte data); - boolean i2cWriteMultiple(byte address, uint8_t *dataBytes, size_t numDataBytes); - void parallelSetup(); - void parallelWrite(byte data, byte dc); +}; + +class MicroOLED_SPI : public MicroOLEDBase +{ + friend class MicroOLEDBase; +public: + MicroOLED_SPI(uint8_t rst, uint8_t dc, uint8_t cs); + + boolean begin(void); // Needed for backward-compatibility + boolean begin(SPIClass & spiPort); // User-defined SPIClass + +private: + void command(uint8_t c); + void data(uint8_t c); + void constantData(uint8_t c, uint8_t n); + void multipleData(uint8_t * buf, uint8_t n); + + void spiTransfer(byte data); + void spiSetup(SPIClass & spiPort = SPI); + + SPIClass * _spiPort; // The generic connection to user's chosen SPI hardware +}; + +class MicroOLED_I2C : public MicroOLEDBase +{ + friend class MicroOLEDBase; +public: + MicroOLED_I2C(uint8_t rst); // leaving the address currently undefined + MicroOLED_I2C(uint8_t rst, uint8_t dc); + + boolean begin(void); // Needed for backward-compatibility + boolean begin(uint8_t deviceAddress, TwoWire & wirePort); // User-defined I2C address + // and TwoWire + + // Control the size of the internal I2C transaction amount + void setI2CTransactionSize(uint8_t bufferSize); + uint8_t getI2CTransactionSize(void); + + // Set the max number of bytes set in a given I2C transaction + uint8_t i2cTransactionSize = 32; // Default to ATmega328 limit + +private: + void i2cSetup(uint8_t deviceAddress = I2C_ADDRESS_UNDEFINED, + TwoWire & wirePort = Wire); + + void command(uint8_t c); + void data(uint8_t c); + void constantData(uint8_t c, uint8_t n); + void multipleData(uint8_t * buf, uint8_t n); + + void i2cWrite(byte address, byte control, byte data); + boolean i2cWriteMultiple(byte address, uint8_t * dataBytes, size_t numDataBytes); - TwoWire *_i2cPort; //The generic connection to user's chosen I2C hardware + byte moled_i2c_address = I2C_ADDRESS_UNDEFINED; - SPIClass *_spiPort; //The generic connection to user's chosen SPI hardware + TwoWire * _i2cPort; // The generic connection to user's chosen I2C hardware }; + +class MicroOLED_Parallel : public MicroOLEDBase +{ + friend class MicroOLEDBase; +public: + MicroOLED_Parallel(uint8_t rst, + uint8_t dc, + uint8_t cs, + uint8_t wr, + uint8_t rd, + uint8_t d0, + uint8_t d1, + uint8_t d2, + uint8_t d3, + uint8_t d4, + uint8_t d5, + uint8_t d6, + uint8_t d7); + + boolean begin(void); // Needed for backward-compatibility + +private: + uint8_t wrPin, rdPin; + uint8_t dPins[8]; + + void command(uint8_t c); + void data(uint8_t c); + void constantData(uint8_t c, uint8_t n); + void multipleData(uint8_t * buf, uint8_t n); + + void parallelSetup(); + void parallelWrite(byte data, byte dc); +}; + #endif diff --git a/src/hardware.cpp b/src/hardware.cpp index 6fbad6d..09892eb 100644 --- a/src/hardware.cpp +++ b/src/hardware.cpp @@ -45,7 +45,7 @@ SPISettings oledSettings(10000000, MSBFIRST, SPI_MODE0); Sets up the SPI pins, initializes the Arduino's SPI interface. **/ -void MicroOLED::spiSetup(SPIClass &spiPort) +void MicroOLED_SPI::spiSetup(SPIClass &spiPort) { // Initialize the pins: pinMode(dcPin, OUTPUT); //dc Is used for SPI and parallel interfaces but not I2C @@ -59,7 +59,37 @@ void MicroOLED::spiSetup(SPIClass &spiPort) #endif _spiPort = &spiPort; _spiPort->begin(); - moled_interface = MOLED_MODE_SPI; // Just in case moled_interface was undefined +} + + +void MicroOLED_SPI::command(uint8_t c) +{ + digitalWrite(dcPin, LOW); + ; // DC pin LOW for a command + spiTransfer(c); // Transfer the command byte +} + +void MicroOLED_SPI::data(uint8_t c) +{ + digitalWrite(dcPin, HIGH); // DC HIGH for a data byte + + spiTransfer(c); // Transfer the data byte +} + +void MicroOLED_SPI::constantData(uint8_t c, uint8_t n) +{ + for (int j = 0; j < n; j++) + { + data(c); + } +} + +void MicroOLED_SPI::multipleData(uint8_t * buf, uint8_t n) +{ + for (int j = 0; j < n; j++) + { + data(*(buf+j)); + } } /** \brief Transfer a byte over SPI @@ -67,7 +97,7 @@ void MicroOLED::spiSetup(SPIClass &spiPort) Use the SPI library to transfer a byte. Only used for data OUTPUT. This function does not toggle the CS pin. Do that before and after! **/ -void MicroOLED::spiTransfer(byte data) +void MicroOLED_SPI::spiTransfer(byte data) { _spiPort->beginTransaction(oledSettings); digitalWrite(csPin, LOW); @@ -80,12 +110,39 @@ void MicroOLED::spiTransfer(byte data) This function initializes the I2C peripheral. **/ -void MicroOLED::i2cSetup(uint8_t deviceAddress, TwoWire &wirePort) +void MicroOLED_I2C::i2cSetup(uint8_t deviceAddress, TwoWire &wirePort) { _i2cPort = &wirePort; if (deviceAddress != I2C_ADDRESS_UNDEFINED) moled_i2c_address = deviceAddress; - moled_interface = MOLED_MODE_I2C; // Just in case moled_interface was undefined +} + + +void MicroOLED_I2C::command(uint8_t c) +{ + // Write to our address, make sure it knows we're sending a + // command: + i2cWrite(moled_i2c_address, I2C_COMMAND, c); +} + +void MicroOLED_I2C::data(uint8_t c) +{ + // Write to our address, make sure it knows we're sending a + // data byte: + i2cWrite(moled_i2c_address, I2C_DATA, c); +} + +void MicroOLED_I2C::constantData(uint8_t c, uint8_t n) +{ + uint8_t zeros[n]; + memset(zeros, 0, n); + i2cWriteMultiple(moled_i2c_address, (uint8_t *)&zeros, 0x80); +} + + +void MicroOLED_I2C::multipleData(uint8_t * buf, uint8_t n) +{ + i2cWriteMultiple(moled_i2c_address, buf, 0x40); } /** \brief Write a byte over I2C @@ -94,7 +151,7 @@ void MicroOLED::i2cSetup(uint8_t deviceAddress, TwoWire &wirePort) the data being sent is a command or display data. Use either I2C_COMMAND or I2C_DATA in that parameter. The data byte can be any 8-bit value. **/ -void MicroOLED::i2cWrite(byte address, byte dc, byte data) +void MicroOLED_I2C::i2cWrite(byte address, byte dc, byte data) { _i2cPort->beginTransmission(address); _i2cPort->write(dc); // If data dc = 0, if command dc = 0x40 @@ -107,7 +164,7 @@ void MicroOLED::i2cWrite(byte address, byte dc, byte data) Write multiple bytes to I2C device _address_. Returns true if all numDataBytes were written successfully **/ -boolean MicroOLED::i2cWriteMultiple(uint8_t address, uint8_t *dataBytes, size_t numDataBytes) +boolean MicroOLED_I2C::i2cWriteMultiple(uint8_t address, uint8_t *dataBytes, size_t numDataBytes) { // I2C: split the data up into packets of i2cTransactionSize size_t bytesLeftToWrite = numDataBytes; @@ -149,7 +206,7 @@ boolean MicroOLED::i2cWriteMultiple(uint8_t address, uint8_t *dataBytes, size_t This function initializes all of the pins used in the parallel interface. **/ -void MicroOLED::parallelSetup() +void MicroOLED_Parallel::parallelSetup() { pinMode(dcPin, OUTPUT); //dc Is used for SPI and parallel interfaces but not I2C @@ -162,8 +219,35 @@ void MicroOLED::parallelSetup() digitalWrite(csPin, HIGH); for (int i = 0; i < 8; i++) pinMode(dPins[i], OUTPUT); +} + + +void MicroOLED_Parallel::command(uint8_t c) +{ + // Write the byte to our parallel interface. Set DC LOW. + parallelWrite(c, LOW); +} + +void MicroOLED_Parallel::data(uint8_t c) +{ + // Write the byte to our parallel interface. Set DC HIGH. + parallelWrite(c, HIGH); +} + +void MicroOLED_Parallel::constantData(uint8_t c, uint8_t n) +{ + for (int j = 0; j < n; j++) + { + data(c); + } +} - moled_interface = MOLED_MODE_PARALLEL; // Just in case moled_interface was undefined +void MicroOLED_Parallel::multipleData(uint8_t * buf, uint8_t n) +{ + for (int j = 0; j < n; j++) + { + data(*(buf+j)); + } } /** \brief Write a byte over the parallel interface @@ -172,7 +256,7 @@ void MicroOLED::parallelSetup() command byte is being sent, and it will toggle the WR, RD and data pins to send a byte. **/ -void MicroOLED::parallelWrite(byte data, byte dc) +void MicroOLED_Parallel::parallelWrite(byte data, byte dc) { // Initial state: cs high, wr high, rd high //digitalWrite(csPin, HIGH);