four-line-display: support for SSD1309 and SSD1327 (SPI only)

* driver for SSD1309 4-Wire SPI (128x64) - untested
* driver for SSD1327 4-Wire SPI (128x128) - tested with waveshare 1.5in grayscale OLED
* driver Info to "clock mode" (WLED_DEBUG only)
* correct some misleading comments
* fix some typos
This commit is contained in:
Frank
2023-03-26 14:38:14 +02:00
parent 361a3b1cb3
commit 049d0f901d

View File

@@ -4,36 +4,35 @@
#include <Wire.h> #include <Wire.h>
#include <SPI.h> #include <SPI.h>
#undef U8X8_NO_HW_I2C // WLEDMM: we do want I2C hardware drivers - if possible #undef U8X8_NO_HW_I2C // WLEDMM: we do want I2C hardware drivers - if possible
//#define WIRE_INTERFACES_COUNT 2 // experimental - tell U8x8Lib that there is a second Wire unit //#define WIRE_INTERFACES_COUNT 2 // experimental - tell U8x8Lib that there is a second Wire unit
#include "wled.h" #include "wled.h"
#include <U8x8lib.h> // from https://github.com/olikraus/u8g2/ #include <U8x8lib.h> // from https://github.com/olikraus/u8g2/
#include "4LD_wled_fonts.c" #include "4LD_wled_fonts.c"
#ifndef FLD_ESP32_NO_THREADS #ifndef FLD_ESP32_NO_THREADS
#define FLD_ESP32_USE_THREADS // comment out to use 0.13.x behviour without parallel update task - slower, but more robust. May delay other tasks like LEDs or audioreactive!! #define FLD_ESP32_USE_THREADS // comment out to use 0.13.x behaviour without parallel update task - slower, but more robust. May delay other tasks like LEDs or audioreactive!!
#endif #endif
//#define OLD_4LD_FONTS // comment out if you prefer the "classic" look with blocky fonts (saves 1K flash) //#define OLD_4LD_FONTS // comment out if you prefer the "classic" look with blocky fonts (saves 1K flash)
// //
// Insired by the usermod_v2_four_line_display // Inspired by the usermod_v2_four_line_display
// //
// v2 usermod for using 128x32 or 128x64 i2c // v2 usermod for using 128x32 or 128x64 i2c
// OLED displays to provide a four line display // OLED displays to provide a four line display
// for WLED. // for WLED.
// //
// Dependencies // Dependencies
// * This usermod REQURES the ModeSortUsermod // * This usermod does not REQUIRE the ModeSortUsermod any more
// * This Usermod works best, by far, when coupled // * This usermod works best, by far, when coupled
// with RotaryEncoderUIUsermod. // with RotaryEncoderUI_ALT usermod.
// //
// Make sure to enable NTP and set your time zone in WLED Config | Time. // Make sure to enable NTP and set your time zone in WLED Config | Time.
// //
// REQUIREMENT: You must add the following requirements to // REQUIREMENT: You must add the following requirements to
// REQUIREMENT: "lib_deps" within platformio.ini / platformio_override.ini // REQUIREMENT: "lib_deps" within platformio.ini / platformio_override.ini
// REQUIREMENT: * U8g2 (the version already in platformio.ini is fine) // REQUIREMENT: olikraus/U8g2@ ^2.34.15 (the version already in platformio.ini is fine)
// REQUIREMENT: * Wire
// //
//The SCL and SDA pins are defined here. //The SCL and SDA pins are defined here.
@@ -95,7 +94,9 @@ typedef enum {
SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C SSD1305, // U8X8_SSD1305_128X32_ADAFRUIT_HW_I2C
SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C SSD1305_64, // U8X8_SSD1305_128X64_ADAFRUIT_HW_I2C
SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI SSD1306_SPI, // U8X8_SSD1306_128X32_NONAME_HW_SPI
SSD1306_SPI64 // U8X8_SSD1306_128X64_NONAME_HW_SPI SSD1306_SPI64=7, // U8X8_SSD1306_128X64_NONAME_HW_SPI
SSD1309_SPI64=8, // U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI
SSD1327_SPI128=9 // U8X8_SSD1327_WS_128X128_4W_SW_SPI
} DisplayType; } DisplayType;
@@ -163,6 +164,8 @@ class FourLineDisplayUsermod : public Usermod {
bool enabled = true; bool enabled = true;
#endif #endif
bool contrastFix = false; bool contrastFix = false;
bool driverHW = false;
bool driverSPI = false;
// Next variables hold the previous known values to determine if redraw is // Next variables hold the previous known values to determine if redraw is
// required. // required.
@@ -240,8 +243,8 @@ class FourLineDisplayUsermod : public Usermod {
if (u8x8 == nullptr) return; if (u8x8 == nullptr) return;
if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex
#if defined(ARDUINO_ARCH_ESP32) && !defined(OLD_4LD_FONTS) // WLEDMM use nicer 2x2 font on ESP32 #if defined(ARDUINO_ARCH_ESP32) && !defined(OLD_4LD_FONTS) // WLEDMM use nicer 2x2 font on ESP32
if (!ignoreLH && lineHeight==2) { if (!ignoreLH && lineHeight>1) {
if(strlen(string) > 3) // WLEDMM little hack - less than 3 chars -> show in bold if(strlen(string) > 3) // WLEDMM little hack - less than 3 chars -> show in bold
u8x8->setFont(u8x8_font_7x14_1x2_r); // normal u8x8->setFont(u8x8_font_7x14_1x2_r); // normal
else else
@@ -254,8 +257,8 @@ class FourLineDisplayUsermod : public Usermod {
} }
#else #else
u8x8->setFont(u8x8_font_chroma48medium8_r); u8x8->setFont(u8x8_font_chroma48medium8_r);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2String(col, row, string); if (!ignoreLH && lineHeight>1) u8x8->draw1x2String(col, row, string);
else u8x8->drawString(col, row, string); else u8x8->drawString(col, row, string);
#endif #endif
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
} }
@@ -263,8 +266,8 @@ class FourLineDisplayUsermod : public Usermod {
if (!typeOK || !enabled) return; if (!typeOK || !enabled) return;
if (u8x8 == nullptr) return; if (u8x8 == nullptr) return;
if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex
#if defined(ARDUINO_ARCH_ESP32) && !defined(OLD_4LD_FONTS) // WLEDMM use nicer 2x2 font on ESP32 #if defined(ARDUINO_ARCH_ESP32) && !defined(OLD_4LD_FONTS) // WLEDMM use nicer 2x2 font on ESP32
if (lineHeight==2) { // WLEDMM use 2x3 on 128x64 displays if (lineHeight>1) { // WLEDMM use 2x3 on 128x64 displays
//u8x8->setFont(u8x8_font_profont29_2x3_r); // sans serif 2x3 //u8x8->setFont(u8x8_font_profont29_2x3_r); // sans serif 2x3
u8x8->setFont(u8x8_font_courB18_2x3_r); // courier bold 2x3 u8x8->setFont(u8x8_font_courB18_2x3_r); // courier bold 2x3
u8x8->drawString(col, row + (row >3? 1:0), string); u8x8->drawString(col, row + (row >3? 1:0), string);
@@ -276,7 +279,7 @@ class FourLineDisplayUsermod : public Usermod {
} }
#else #else
u8x8->setFont(u8x8_font_chroma48medium8_r); u8x8->setFont(u8x8_font_chroma48medium8_r);
if (lineHeight==2) { // WLEDMM use 2x3 on 128x64 displays if (lineHeight>1) { // WLEDMM use 2x3 on 128x64 displays
u8x8->draw2x2String(col, row + (row >3? 1:0), string); u8x8->draw2x2String(col, row + (row >3? 1:0), string);
} else { } else {
u8x8->draw2x2String(col, row, string); u8x8->draw2x2String(col, row, string);
@@ -288,7 +291,7 @@ class FourLineDisplayUsermod : public Usermod {
if (!typeOK || !enabled) return; if (!typeOK || !enabled) return;
if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex if (FLD_SemaphoreTake(drawMux, maxWait) != pdTRUE) return; // WLEDMM acquire draw mutex
u8x8->setFont(font); u8x8->setFont(font);
if (!ignoreLH && lineHeight==2) u8x8->draw1x2Glyph(col, row, glyph); if (!ignoreLH && lineHeight>1) u8x8->draw1x2Glyph(col, row, glyph);
else u8x8->drawGlyph(col, row, glyph); else u8x8->drawGlyph(col, row, glyph);
FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex FLD_SemaphoreGive(drawMux); // WLEDMM release draw mutex
} }
@@ -325,7 +328,7 @@ class FourLineDisplayUsermod : public Usermod {
void draw2x2GlyphIcons() { void draw2x2GlyphIcons() {
if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it if (!typeOK || !enabled) return; // WLEDMM make sure the display is initialized before we try to draw on it
if (FLD_SemaphoreTake(drawMuxBig, maxWaitLong) != pdTRUE) return; // WLEDMM acquire BIG draw mutex if (FLD_SemaphoreTake(drawMuxBig, maxWaitLong) != pdTRUE) return; // WLEDMM acquire BIG draw mutex
if (lineHeight == 2) { if (lineHeight > 1) {
drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x2, true); //brightness icon drawGlyph( 1, 0, 1, u8x8_4LineDisplay_WLED_icons_2x2, true); //brightness icon
drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x2, true); //speed icon drawGlyph( 5, 0, 2, u8x8_4LineDisplay_WLED_icons_2x2, true); //speed icon
drawGlyph( 9, 0, 3, u8x8_4LineDisplay_WLED_icons_2x2, true); //intensity icon drawGlyph( 9, 0, 3, u8x8_4LineDisplay_WLED_icons_2x2, true); //intensity icon
@@ -379,7 +382,11 @@ class FourLineDisplayUsermod : public Usermod {
drawStatusIcons(); //icons power, wifi, timer, etc drawStatusIcons(); //icons power, wifi, timer, etc
if (lineHeight > 1) { // WLEDMM use extra space for useful information if (lineHeight > 1) { // WLEDMM use extra space for useful information
strncpy_P(lineBuffer, PSTR(" "), LINE_BUFFER_SIZE); #if defined(WLED_DEBUG) || defined(SR_DEBUG) || defined(SR_STATS)
snprintf_P(lineBuffer, LINE_BUFFER_SIZE, PSTR(" %-3.3s %-2.2s "), driverSPI? "SPI" : "I2C", driverHW? "hw" : "sw"); // WLEDMM driver info
#else
strncpy_P(lineBuffer, PSTR(" "), LINE_BUFFER_SIZE);
#endif
if (apActive) strncpy_P(lineBuffer, PSTR(" AP mode "), LINE_BUFFER_SIZE); if (apActive) strncpy_P(lineBuffer, PSTR(" AP mode "), LINE_BUFFER_SIZE);
else if (!WLED_CONNECTED) strncpy_P(lineBuffer, PSTR(" NO NET "), LINE_BUFFER_SIZE); else if (!WLED_CONNECTED) strncpy_P(lineBuffer, PSTR(" NO NET "), LINE_BUFFER_SIZE);
if (WLED_MQTT_CONNECTED) lineBuffer[9] = 'M'; // "MQTT" if (WLED_MQTT_CONNECTED) lineBuffer[9] = 'M'; // "MQTT"
@@ -411,7 +418,7 @@ class FourLineDisplayUsermod : public Usermod {
void setup() { void setup() {
if (!enabled) return; // typeOK = true will be set after successful setup if (!enabled) return; // typeOK = true will be set after successful setup
bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); bool isHW, isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type > 7);
PinOwner po = PinOwner::UM_FourLineDisplay; PinOwner po = PinOwner::UM_FourLineDisplay;
if (isSPI) { if (isSPI) {
if (ioPin[0] < 0 || ioPin[1] < 0) { if (ioPin[0] < 0 || ioPin[1] < 0) {
@@ -467,6 +474,8 @@ class FourLineDisplayUsermod : public Usermod {
} }
} }
driverHW = isHW;
driverSPI= isSPI;
DEBUG_PRINTLN(F("Allocating display.")); DEBUG_PRINTLN(F("Allocating display."));
/* /*
// At some point it may be good to not new/delete U8X8 object but use this instead // At some point it may be good to not new/delete U8X8 object but use this instead
@@ -538,6 +547,16 @@ class FourLineDisplayUsermod : public Usermod {
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]); if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset else u8x8 = (U8X8 *) new U8X8_SSD1306_128X64_NONAME_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
break; break;
case SSD1309_SPI64:
// u8x8 uses global SPI variable that is attached to VSPI bus on ESP32
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME0_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else u8x8 = (U8X8 *) new U8X8_SSD1309_128X64_NONAME0_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
break;
case SSD1327_SPI128:
// u8x8 uses global SPI variable that is attached to VSPI bus on ESP32
if (!isHW) u8x8 = (U8X8 *) new U8X8_SSD1327_WS_128X128_4W_SW_SPI(ioPin[0], ioPin[1], ioPin[2], ioPin[3], ioPin[4]);
else u8x8 = (U8X8 *) new U8X8_SSD1327_WS_128X128_4W_HW_SPI(ioPin[2], ioPin[3], ioPin[4]); // Pins are cs, dc, reset
break;
default: default:
u8x8 = nullptr; u8x8 = nullptr;
} }
@@ -551,6 +570,8 @@ class FourLineDisplayUsermod : public Usermod {
} }
lineHeight = u8x8->getRows() > 4 ? 2 : 1; lineHeight = u8x8->getRows() > 4 ? 2 : 1;
if (u8x8->getRows() > 8) lineHeight =3;
//if (u8x8->getRows() > 12) lineHeight =4;
if (isSPI) { if (isSPI) {
USER_PRINTLN(isHW ? F("Starting display (SPI HW).") : F("Starting display (SPI Soft).")); USER_PRINTLN(isHW ? F("Starting display (SPI HW).") : F("Starting display (SPI Soft)."));
} else { } else {
@@ -568,7 +589,7 @@ class FourLineDisplayUsermod : public Usermod {
setPowerSave(0); setPowerSave(0);
drawing = false; drawing = false;
// init semaphores to enable drawing // init semaphores to allow drawing
FLD_SemaphoreGive(drawMux); FLD_SemaphoreGive(drawMux);
FLD_SemaphoreGive(drawMuxBig); FLD_SemaphoreGive(drawMuxBig);
@@ -798,9 +819,9 @@ class FourLineDisplayUsermod : public Usermod {
uint8_t col = 15; uint8_t col = 15;
uint8_t row = 0; uint8_t row = 0;
drawGlyph(col, row, (wificonnected ? 20 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // wifi icon drawGlyph(col, row, (wificonnected ? 20 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // wifi icon
if (lineHeight==2) { col--; } else { row++; } if (lineHeight>1) { col--; } else { row++; }
drawGlyph(col, row, (bri > 0 ? 9 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // power icon drawGlyph(col, row, (bri > 0 ? 9 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // power icon
if (lineHeight==2) { col--; } else { col = row = 0; } if (lineHeight>1) { col--; } else { col = row = 0; }
drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nightlight mode drawGlyph(col, row, (nightlightActive ? 6 : 0), u8x8_4LineDisplay_WLED_icons_1x1, true); // moon icon for nightlight mode
} }
@@ -839,7 +860,7 @@ class FourLineDisplayUsermod : public Usermod {
for (byte i=5; i<=printedChars; i++) lineBuffer[i-5] = lineBuffer[i]; //include '\0' for (byte i=5; i<=printedChars; i++) lineBuffer[i-5] = lineBuffer[i]; //include '\0'
printedChars -= 5; printedChars -= 5;
} }
if (lineHeight == 2) { // use this code for 8 line display if (lineHeight > 1) { // use this code for 8 line display
char smallBuffer1[MAX_MODE_LINE_SPACE+1] = { '\0' }; char smallBuffer1[MAX_MODE_LINE_SPACE+1] = { '\0' };
char smallBuffer2[MAX_MODE_LINE_SPACE+1] = { '\0' }; char smallBuffer2[MAX_MODE_LINE_SPACE+1] = { '\0' };
uint8_t smallChars1 = 0; uint8_t smallChars1 = 0;
@@ -868,11 +889,11 @@ class FourLineDisplayUsermod : public Usermod {
} }
while (smallChars1 < (MAX_MODE_LINE_SPACE-1)) smallBuffer1[smallChars1++]=' '; while (smallChars1 < (MAX_MODE_LINE_SPACE-1)) smallBuffer1[smallChars1++]=' ';
smallBuffer1[smallChars1] = 0; smallBuffer1[smallChars1] = 0;
smallBuffer1[MAX_MODE_LINE_SPACE -1] = '\0'; // ensure the string ends where it should (while loop avove can overshoot by 1) smallBuffer1[MAX_MODE_LINE_SPACE -1] = '\0'; // ensure the string ends where it should (while loop above can overshoot by 1)
drawString(1, row*lineHeight, smallBuffer1, true); drawString(1, row*lineHeight, smallBuffer1, true);
while (smallChars2 < (MAX_MODE_LINE_SPACE-1)) smallBuffer2[smallChars2++]=' '; while (smallChars2 < (MAX_MODE_LINE_SPACE-1)) smallBuffer2[smallChars2++]=' ';
smallBuffer2[smallChars2] = 0; smallBuffer2[smallChars2] = 0;
smallBuffer2[MAX_MODE_LINE_SPACE -1] = '\0'; // ensure the string ends where it should (while loop avove can overshoot by 1) smallBuffer2[MAX_MODE_LINE_SPACE -1] = '\0'; // ensure the string ends where it should (while loop above can overshoot by 1)
drawString(1, row*lineHeight+1, smallBuffer2, true); drawString(1, row*lineHeight+1, smallBuffer2, true);
} }
} else { // use this code for 4 ling displays } else { // use this code for 4 ling displays
@@ -926,7 +947,7 @@ class FourLineDisplayUsermod : public Usermod {
if (!wakeDisplay()) clear(); if (!wakeDisplay()) clear();
// Print the overlay // Print the overlay
if (glyphType>0 && glyphType<255) { if (glyphType>0 && glyphType<255) {
if (lineHeight == 2) drawGlyph(5, 0, glyphType, u8x8_4LineDisplay_WLED_icons_6x6, true); // use 3x3 font with draw2x2Glyph() if flash runs short and comment out 6x6 font if (lineHeight > 1) drawGlyph(5, 0, glyphType, u8x8_4LineDisplay_WLED_icons_6x6, true); // use 3x3 font with draw2x2Glyph() if flash runs short and comment out 6x6 font
else drawGlyph(6, 0, glyphType, u8x8_4LineDisplay_WLED_icons_3x3, true); else drawGlyph(6, 0, glyphType, u8x8_4LineDisplay_WLED_icons_3x3, true);
} }
if (line1) { if (line1) {
@@ -950,7 +971,7 @@ class FourLineDisplayUsermod : public Usermod {
// Turn the display back on // Turn the display back on
if (!wakeDisplay()) clear(); if (!wakeDisplay()) clear();
// Print the overlay // Print the overlay
if (lineHeight == 2) { if (lineHeight > 1) {
//add a bit of randomness //add a bit of randomness
switch (millis()%3) { switch (millis()%3) {
case 0: case 0:
@@ -1242,7 +1263,9 @@ class FourLineDisplayUsermod : public Usermod {
oappend(SET_F("addOption(dd,'SSD1305 128x64',5);")); oappend(SET_F("addOption(dd,'SSD1305 128x64',5);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI',6);")); oappend(SET_F("addOption(dd,'SSD1306 SPI',6);"));
oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);")); oappend(SET_F("addOption(dd,'SSD1306 SPI 128x64',7);"));
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); oappend(SET_F("addOption(dd,'SSD1309 SPI 128x64',8);"));
oappend(SET_F("addOption(dd,'SSD1327 SPI 128x128',9);"));
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type > 7);
// WLEDMM add defaults // WLEDMM add defaults
oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'','I2C/SPI CLK');")); oappend(SET_F("addInfo('4LineDisplay:pin[]',0,'','I2C/SPI CLK');"));
oappend(SET_F("dRO('4LineDisplay:pin[]',0);")); // disable read only pins oappend(SET_F("dRO('4LineDisplay:pin[]',0);")); // disable read only pins
@@ -1305,7 +1328,7 @@ class FourLineDisplayUsermod : public Usermod {
void addToConfig(JsonObject& root) { void addToConfig(JsonObject& root) {
// determine if we are using global HW pins (data & clock) // determine if we are using global HW pins (data & clock)
int8_t hw_dta, hw_clk; int8_t hw_dta, hw_clk;
if ((type == SSD1306_SPI || type == SSD1306_SPI64)) { if ((type == SSD1306_SPI || type == SSD1306_SPI64) || (type > 7)) {
hw_clk = spi_sclk; hw_clk = spi_sclk;
hw_dta = spi_mosi; hw_dta = spi_mosi;
} else { } else {
@@ -1371,7 +1394,7 @@ class FourLineDisplayUsermod : public Usermod {
clockMode = top[FPSTR(_clockMode)] | clockMode; clockMode = top[FPSTR(_clockMode)] | clockMode;
showSeconds = top[FPSTR(_showSeconds)] | showSeconds; showSeconds = top[FPSTR(_showSeconds)] | showSeconds;
contrastFix = top[FPSTR(_contrastFix)] | contrastFix; contrastFix = top[FPSTR(_contrastFix)] | contrastFix;
if (newType == SSD1306_SPI || newType == SSD1306_SPI64) if (newType == SSD1306_SPI || newType == SSD1306_SPI64 || newType > 7)
ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency ioFrequency = min(20000, max(500, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
else else
ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency ioFrequency = min(3400, max(100, (int)(top[FPSTR(_busClkFrequency)] | ioFrequency/1000))) * 1000; // limit frequency
@@ -1401,7 +1424,7 @@ class FourLineDisplayUsermod : public Usermod {
USER_PRINTLN(F("Display terminated.")); USER_PRINTLN(F("Display terminated."));
} }
PinOwner po = PinOwner::UM_FourLineDisplay; PinOwner po = PinOwner::UM_FourLineDisplay;
bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64); bool isSPI = (type == SSD1306_SPI || type == SSD1306_SPI64 || type > 7);
if (isSPI) { if (isSPI) {
pinManager.deallocateMultiplePins((const uint8_t *)(&oldPin[2]), 3, po); pinManager.deallocateMultiplePins((const uint8_t *)(&oldPin[2]), 3, po);
bool isHW = (oldPin[0]==spi_sclk && oldPin[1]==spi_mosi); bool isHW = (oldPin[0]==spi_sclk && oldPin[1]==spi_mosi);