From 53ba4bbddfd8fb6dd03e8eaf7e7bb4c224f5e7ad Mon Sep 17 00:00:00 2001 From: moononournation Date: Sun, 27 Dec 2020 23:52:28 +0800 Subject: [PATCH] improve ESP8266 performance --- library.properties | 2 +- src/databus/Arduino_HWSPI.cpp | 50 +++---- src/databus/Arduino_HWSPI.h | 50 +++---- src/databus/Arduino_SWSPI.cpp | 248 ++++++++++++++++++---------------- src/databus/Arduino_SWSPI.h | 56 ++++---- 5 files changed, 213 insertions(+), 193 deletions(-) diff --git a/library.properties b/library.properties index ec8220ca..43b1a897 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=GFX Library for Arduino -version=1.0.1 +version=1.0.3 author=Moon On Our Nation maintainer=Moon On Our Nation sentence=Arduino graphics library for various displays with various data bus interfaces diff --git a/src/databus/Arduino_HWSPI.cpp b/src/databus/Arduino_HWSPI.cpp index 08885981..be82173a 100644 --- a/src/databus/Arduino_HWSPI.cpp +++ b/src/databus/Arduino_HWSPI.cpp @@ -57,29 +57,7 @@ void Arduino_HWSPI::begin(int32_t speed, int8_t dataMode) #if defined(USE_FAST_PINIO) #if defined(HAS_PORT_SET_CLR) -#if defined(CORE_TEENSY) -#if !defined(KINETISK) - dcPinMask = digitalPinToBitMask(_dc); -#endif - dcPortSet = portSetRegister(_dc); - dcPortClr = portClearRegister(_dc); - if (_cs >= 0) - { -#if !defined(KINETISK) - csPinMask = digitalPinToBitMask(_cs); -#endif - csPortSet = portSetRegister(_cs); - csPortClr = portClearRegister(_cs); - } - else - { -#if !defined(KINETISK) - csPinMask = 0; -#endif - csPortSet = dcPortSet; - csPortClr = dcPortClr; - } -#elif defined(ESP32) +#if defined(ESP32) dcPinMask = digitalPinToBitMask(_dc); if (_dc >= 32) { @@ -113,6 +91,28 @@ void Arduino_HWSPI::begin(int32_t speed, int8_t dataMode) csPortClr = (PORTreg_t)dcPortClr; csPinMask = 0; } +#elif defined(CORE_TEENSY) +#if !defined(KINETISK) + dcPinMask = digitalPinToBitMask(_dc); +#endif + dcPortSet = portSetRegister(_dc); + dcPortClr = portClearRegister(_dc); + if (_cs >= 0) + { +#if !defined(KINETISK) + csPinMask = digitalPinToBitMask(_cs); +#endif + csPortSet = portSetRegister(_cs); + csPortClr = portClearRegister(_cs); + } + else + { +#if !defined(KINETISK) + csPinMask = 0; +#endif + csPortSet = dcPortSet; + csPortClr = dcPortClr; + } #else // !CORE_TEENSY dcPinMask = digitalPinToBitMask(_dc); dcPortSet = &(PORT->Group[g_APinDescription[_dc].ulPort].OUTSET.reg); @@ -438,7 +438,7 @@ void Arduino_HWSPI::writePattern(uint8_t *data, uint8_t len, uint32_t repeat) { #if defined(ESP8266) || defined(ESP32) HWSPI.writePattern(data, len, repeat); -#else +#else // !(defined(ESP8266) || defined(ESP32)) while (repeat--) { for (uint8_t i = 0; i < len; i++) @@ -446,7 +446,7 @@ void Arduino_HWSPI::writePattern(uint8_t *data, uint8_t len, uint32_t repeat) write(data[i]); } } -#endif +#endif // !(defined(ESP8266) || defined(ESP32)) } /******** low level bit twiddling **********/ diff --git a/src/databus/Arduino_HWSPI.h b/src/databus/Arduino_HWSPI.h index 14945584..0a5c01e3 100644 --- a/src/databus/Arduino_HWSPI.h +++ b/src/databus/Arduino_HWSPI.h @@ -8,51 +8,53 @@ // HARDWARE CONFIG --------------------------------------------------------- #if defined(__AVR__) -typedef uint8_t ARDUINOGFX_PORT_t; ///< PORT values are 8-bit -#define USE_FAST_PINIO ///< Use direct PORT register access -#elif defined(ARDUINO_STM32_FEATHER) // WICED -typedef class HardwareSPI SPIClass; ///< SPI is a bit odd on WICED -typedef uint32_t ARDUINOGFX_PORT_t; ///< PORT values are 32-bit +typedef uint8_t ARDUINOGFX_PORT_t; +#define USE_FAST_PINIO ///< Use direct PORT register access +#elif defined(ESP32) +typedef uint32_t ARDUINOGFX_PORT_t; +#define USE_FAST_PINIO ///< Use direct PORT register access +#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers +#elif defined(ESP8266) +typedef uint32_t ARDUINOGFX_PORT_t; +#define USE_FAST_PINIO ///< Use direct PORT register access +#elif defined(ARDUINO_STM32_FEATHER) +typedef uint32_t ARDUINOGFX_PORT_t; #elif defined(__arm__) #if defined(ARDUINO_ARCH_SAMD) // Adafruit M0, M4 -typedef uint32_t ARDUINOGFX_PORT_t; ///< PORT values are 32-bit +typedef uint32_t ARDUINOGFX_PORT_t; #define USE_FAST_PINIO ///< Use direct PORT register access #define HAS_PORT_SET_CLR ///< PORTs have set & clear registers #elif defined(CORE_TEENSY) +#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // PJRC Teensy 4.x -#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x -typedef uint32_t ARDUINOGFX_PORT_t; ///< PORT values are 32-bit - // PJRC Teensy 3.x +typedef uint32_t ARDUINOGFX_PORT_t; #else -typedef uint8_t ARDUINOGFX_PORT_t; ///< PORT values are 8-bit +// PJRC Teensy 3.x +typedef uint8_t ARDUINOGFX_PORT_t; #endif #define USE_FAST_PINIO ///< Use direct PORT register access #define HAS_PORT_SET_CLR ///< PORTs have set & clear registers #else // Arduino Due? -typedef uint32_t ARDUINOGFX_PORT_t; ///< PORT values are 32-bit - // USE_FAST_PINIO not available here (yet)...Due has a totally different - // GPIO register set and will require some changes elsewhere (e.g. in - // constructors especially). -#endif -#elif defined(ESP32) typedef uint32_t ARDUINOGFX_PORT_t; -#define USE_FAST_PINIO ///< Use direct PORT register access -#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers -#else // !ARM -// Probably ESP8266. USE_FAST_PINIO is not available here (yet) +// USE_FAST_PINIO not available here (yet)...Due has a totally different +// GPIO register set and will require some changes elsewhere (e.g. in +// constructors especially). +#endif +#else // !ARM +// Unknow architecture, USE_FAST_PINIO is not available here (yet) // but don't worry about it too much...the digitalWrite() implementation // on these platforms is reasonably efficient and already RAM-resident, // only gotcha then is no parallel connection support for now. -typedef uint32_t ARDUINOGFX_PORT_t; ///< PORT values are 32-bit -#endif // end !ARM -typedef volatile ARDUINOGFX_PORT_t *PORTreg_t; ///< PORT register type +typedef uint32_t ARDUINOGFX_PORT_t; +#endif // end !ARM +typedef volatile ARDUINOGFX_PORT_t *PORTreg_t; #if defined(ADAFRUIT_PYPORTAL) || defined(ADAFRUIT_PYBADGE_M4_EXPRESS) || defined(ADAFRUIT_PYGAMER_M4_EXPRESS) || defined(ADAFRUIT_HALLOWING_M4_EXPRESS) #define USE_SPI_DMA ///< Auto DMA if using PyPortal #else - //#define USE_SPI_DMA ///< If set, use DMA if available +//#define USE_SPI_DMA ///< If set, use DMA if available #endif // Another "oops" name -- this now also handles parallel DMA. // If DMA is enabled, Arduino sketch MUST #include diff --git a/src/databus/Arduino_SWSPI.cpp b/src/databus/Arduino_SWSPI.cpp index bdb391d0..7141934e 100644 --- a/src/databus/Arduino_SWSPI.cpp +++ b/src/databus/Arduino_SWSPI.cpp @@ -6,12 +6,8 @@ #include "Arduino_SWSPI.h" Arduino_SWSPI::Arduino_SWSPI(int8_t dc, int8_t cs, int8_t sck, int8_t mosi, int8_t miso /* = -1 */) + : _dc(dc), _cs(cs), _sck(sck), _mosi(mosi), _miso(miso) { - _dc = dc; - _cs = cs; - _sck = sck; - _mosi = mosi; - _miso = miso; } void Arduino_SWSPI::begin(int32_t speed, int8_t dataMode) @@ -37,59 +33,7 @@ void Arduino_SWSPI::begin(int32_t speed, int8_t dataMode) #if defined(USE_FAST_PINIO) #if defined(HAS_PORT_SET_CLR) -#if defined(CORE_TEENSY) -#if !defined(KINETISK) - sckPinMask = digitalPinToBitMask(_sck); - mosiPinMask = digitalPinToBitMask(_mosi); -#endif - sckPortSet = portSetRegister(_sck); - sckPortClr = portClearRegister(_sck); - mosiPortSet = portSetRegister(_mosi); - mosiPortClr = portClearRegister(_mosi); - if (_dc >= 0) - { -#if !defined(KINETISK) - dcPinMask = digitalPinToBitMask(_dc); -#endif - dcPortSet = portSetRegister(_dc); - dcPortClr = portClearRegister(_dc); - } - else - { -#if !defined(KINETISK) - dcPinMask = 0; -#endif - dcPortSet = sckPortSet; - dcPortClr = sckPortClr; - } - if (_cs >= 0) - { -#if !defined(KINETISK) - csPinMask = digitalPinToBitMask(_cs); -#endif - csPortSet = portSetRegister(_cs); - csPortClr = portClearRegister(_cs); - } - else - { -#if !defined(KINETISK) - csPinMask = 0; -#endif - csPortSet = sckPortSet; - csPortClr = sckPortClr; - } - if (_miso >= 0) - { - misoPort = portInputRegister(_miso); -#if !defined(KINETISK) - misoPinMask = digitalPinToBitMask(_miso); -#endif - } - else - { - misoPort = portInputRegister(_miso); - } -#elif defined(ESP32) +#if defined(ESP32) sckPinMask = digitalPinToBitMask(_sck); mosiPinMask = digitalPinToBitMask(_mosi); if (_sck >= 32) @@ -112,28 +56,17 @@ void Arduino_SWSPI::begin(int32_t speed, int8_t dataMode) mosiPortSet = (PORTreg_t)&GPIO.out_w1ts; mosiPortClr = (PORTreg_t)&GPIO.out_w1tc; } + dcPinMask = digitalPinToBitMask(_dc); if (_dc >= 32) { - dcPinMask = digitalPinToBitMask(_dc); dcPortSet = (PORTreg_t)&GPIO.out1_w1ts.val; dcPortClr = (PORTreg_t)&GPIO.out1_w1tc.val; } - else if (_dc >= 0) + else { - dcPinMask = digitalPinToBitMask(_dc); dcPortSet = (PORTreg_t)&GPIO.out_w1ts; dcPortClr = (PORTreg_t)&GPIO.out_w1tc; } - else - { - // No D/C line defined; 9-bit SPI. - // Assign a valid GPIO register (though not used for DC), and an - // empty pin bitmask...the nonsense bit-twiddling might be faster - // than checking _dc and possibly branching. - dcPinMask = 0; - dcPortSet = (PORTreg_t)sckPortSet; - dcPortClr = (PORTreg_t)sckPortClr; - } if (_cs >= 32) { csPinMask = digitalPinToBitMask(_cs); @@ -152,9 +85,20 @@ void Arduino_SWSPI::begin(int32_t speed, int8_t dataMode) // Assign a valid GPIO register (though not used for CS), and an // empty pin bitmask...the nonsense bit-twiddling might be faster // than checking _cs and possibly branching. + csPortSet = (PORTreg_t)dcPortSet; + csPortClr = (PORTreg_t)dcPortClr; csPinMask = 0; - csPortSet = (PORTreg_t)sckPortSet; - csPortClr = (PORTreg_t)sckPortClr; + } + if (_miso >= 0) + { + misoPort = portInputRegister(_miso); +#if !defined(KINETISK) + misoPinMask = digitalPinToBitMask(_miso); +#endif + } + else + { + misoPort = portInputRegister(_miso); } if (_miso >= 0) { @@ -166,7 +110,48 @@ void Arduino_SWSPI::begin(int32_t speed, int8_t dataMode) misoPinMask = 0; misoPort = (PORTreg_t)portInputRegister(digitalPinToPort(_sck)); } -#else // !CORE_TEENSY and !ESP32 +#elif defined(CORE_TEENSY) +#if !defined(KINETISK) + sckPinMask = digitalPinToBitMask(_sck); + mosiPinMask = digitalPinToBitMask(_mosi); +#endif + sckPortSet = portSetRegister(_sck); + sckPortClr = portClearRegister(_sck); + mosiPortSet = portSetRegister(_mosi); + mosiPortClr = portClearRegister(_mosi); + if (_dc >= 0) + { +#if !defined(KINETISK) + dcPinMask = digitalPinToBitMask(_dc); +#endif + dcPortSet = portSetRegister(_dc); + dcPortClr = portClearRegister(_dc); + } + else + { +#if !defined(KINETISK) + dcPinMask = 0; +#endif + dcPortSet = sckPortSet; + dcPortClr = sckPortClr; + } + if (_cs >= 0) + { +#if !defined(KINETISK) + csPinMask = digitalPinToBitMask(_cs); +#endif + csPortSet = portSetRegister(_cs); + csPortClr = portClearRegister(_cs); + } + else + { +#if !defined(KINETISK) + csPinMask = 0; +#endif + csPortSet = sckPortSet; + csPortClr = sckPortClr; + } +#else // !CORE_TEENSY sckPinMask = digitalPinToBitMask(_sck); mosiPinMask = digitalPinToBitMask(_mosi); sckPortSet = &(PORT->Group[g_APinDescription[_sck].ulPort].OUTSET.reg); @@ -217,8 +202,6 @@ void Arduino_SWSPI::begin(int32_t speed, int8_t dataMode) } #endif // end !CORE_TEENSY #else // !HAS_PORT_SET_CLR - dcPort = (PORTreg_t)portOutputRegister(digitalPinToPort(_dc)); - dcPinMaskSet = digitalPinToBitMask(_dc); sckPort = (PORTreg_t)portOutputRegister(digitalPinToPort(_sck)); sckPinMaskSet = digitalPinToBitMask(_sck); mosiPort = (PORTreg_t)portOutputRegister(digitalPinToPort(_mosi)); @@ -427,53 +410,32 @@ void Arduino_SWSPI::writeRepeat(uint16_t p, uint32_t len) { if (_dc < 0) // 9-bit SPI { - if (p == 0xffff) // no need to set MOSI level while filling white - { - SPI_MOSI_HIGH(); - len *= 18; // 9-bit * 2 - while (len--) - { - SPI_SCK_HIGH(); - SPI_SCK_LOW(); - } - } - else +// ESP8266 avoid trigger watchdog +#if defined(ESP8266) + while (len > (ESP8266SAFEBATCHBITSIZE / 9)) { - uint8_t hi = p >> 8; - uint8_t lo = p; - while (len--) - { - WRITE9BITDATA(hi); - WRITE9BITDATA(lo); - } + WRITE9BITREPEAT(p, ESP8266SAFEBATCHBITSIZE / 9); + len -= ESP8266SAFEBATCHBITSIZE / 9; + yield(); } + WRITE9BITREPEAT(p, len); +#else + WRITE9BITREPEAT(p, len); +#endif } else { - if ((p == 0x0000) || (p == 0xffff)) // no need to set MOSI level while filling black or white - { - if (p) - { - SPI_MOSI_HIGH(); - } - else - { - SPI_MOSI_LOW(); - } - len *= 16; - while (len--) - { - SPI_SCK_HIGH(); - SPI_SCK_LOW(); - } - } - else +#if defined(ESP8266) + while (len > (ESP8266SAFEBATCHBITSIZE / 8)) { - while (len--) - { - WRITE16(p); - } + WRITEREPEAT(p, ESP8266SAFEBATCHBITSIZE / 8); + len -= ESP8266SAFEBATCHBITSIZE / 8; + yield(); } + WRITEREPEAT(p, len); +#else + WRITEREPEAT(p, len); +#endif } } @@ -578,8 +540,7 @@ INLINE void Arduino_SWSPI::WRITE(uint8_t d) INLINE void Arduino_SWSPI::WRITE16(uint16_t d) { - uint16_t bit; - bit = 0x8000; + uint16_t bit = 0x8000; while (bit) { if (d & bit) @@ -615,6 +576,57 @@ INLINE void Arduino_SWSPI::WRITE32(uint32_t d) } } +INLINE void Arduino_SWSPI::WRITE9BITREPEAT(uint16_t p, uint32_t len) +{ + if (p == 0xffff) // no need to set MOSI level while filling white + { + SPI_MOSI_HIGH(); + len *= 18; // 9-bit * 2 + while (len--) + { + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + } + } + else + { + uint8_t hi = p >> 8; + uint8_t lo = p; + while (len--) + { + WRITE9BITDATA(hi); + WRITE9BITDATA(lo); + } + } +} + +INLINE void Arduino_SWSPI::WRITEREPEAT(uint16_t p, uint32_t len) +{ + if ((p == 0x0000) || (p == 0xffff)) // no need to set MOSI level while filling black or white + { + if (p) + { + SPI_MOSI_HIGH(); + } + else + { + SPI_MOSI_LOW(); + } + len *= 16; + while (len--) + { + SPI_SCK_HIGH(); + SPI_SCK_LOW(); + } + } + else + { + while (len--) + { + WRITE16(p); + } + } +} /******** low level bit twiddling **********/ INLINE void Arduino_SWSPI::CS_HIGH(void) diff --git a/src/databus/Arduino_SWSPI.h b/src/databus/Arduino_SWSPI.h index 0f46f87a..394fd07d 100644 --- a/src/databus/Arduino_SWSPI.h +++ b/src/databus/Arduino_SWSPI.h @@ -8,51 +8,54 @@ // HARDWARE CONFIG --------------------------------------------------------- #if defined(__AVR__) -typedef uint8_t ARDUINOGFX_PORT_t; ///< PORT values are 8-bit -#define USE_FAST_PINIO ///< Use direct PORT register access -#elif defined(ARDUINO_STM32_FEATHER) // WICED -typedef class HardwareSPI SPIClass; ///< SPI is a bit odd on WICED -typedef uint32_t ARDUINOGFX_PORT_t; ///< PORT values are 32-bit +typedef uint8_t ARDUINOGFX_PORT_t; +#define USE_FAST_PINIO ///< Use direct PORT register access +#elif defined(ESP32) +typedef uint32_t ARDUINOGFX_PORT_t; +#define USE_FAST_PINIO ///< Use direct PORT register access +#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers +#elif defined(ESP8266) +#define ESP8266SAFEBATCHBITSIZE (2048 * 8 * 9) +typedef uint32_t ARDUINOGFX_PORT_t; +#define USE_FAST_PINIO ///< Use direct PORT register access +#elif defined(ARDUINO_STM32_FEATHER) +typedef uint32_t ARDUINOGFX_PORT_t; #elif defined(__arm__) #if defined(ARDUINO_ARCH_SAMD) // Adafruit M0, M4 -typedef uint32_t ARDUINOGFX_PORT_t; ///< PORT values are 32-bit +typedef uint32_t ARDUINOGFX_PORT_t; #define USE_FAST_PINIO ///< Use direct PORT register access #define HAS_PORT_SET_CLR ///< PORTs have set & clear registers #elif defined(CORE_TEENSY) +#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // PJRC Teensy 4.x -#if defined(__IMXRT1052__) || defined(__IMXRT1062__) // Teensy 4.x -typedef uint32_t ARDUINOGFX_PORT_t; ///< PORT values are 32-bit - // PJRC Teensy 3.x +typedef uint32_t ARDUINOGFX_PORT_t; #else -typedef uint8_t ARDUINOGFX_PORT_t; ///< PORT values are 8-bit +// PJRC Teensy 3.x +typedef uint8_t ARDUINOGFX_PORT_t; #endif #define USE_FAST_PINIO ///< Use direct PORT register access #define HAS_PORT_SET_CLR ///< PORTs have set & clear registers #else // Arduino Due? -typedef uint32_t ARDUINOGFX_PORT_t; ///< PORT values are 32-bit - // USE_FAST_PINIO not available here (yet)...Due has a totally different - // GPIO register set and will require some changes elsewhere (e.g. in - // constructors especially). -#endif -#elif defined(ESP32) typedef uint32_t ARDUINOGFX_PORT_t; -#define USE_FAST_PINIO ///< Use direct PORT register access -#define HAS_PORT_SET_CLR ///< PORTs have set & clear registers -#else // !ARM -// Probably ESP8266. USE_FAST_PINIO is not available here (yet) +// USE_FAST_PINIO not available here (yet)...Due has a totally different +// GPIO register set and will require some changes elsewhere (e.g. in +// constructors especially). +#endif +#else // !ARM +// Unknow architecture, USE_FAST_PINIO is not available here (yet) // but don't worry about it too much...the digitalWrite() implementation // on these platforms is reasonably efficient and already RAM-resident, // only gotcha then is no parallel connection support for now. -typedef uint32_t ARDUINOGFX_PORT_t; ///< PORT values are 32-bit -#endif // end !ARM -typedef volatile ARDUINOGFX_PORT_t *PORTreg_t; ///< PORT register type +typedef uint32_t ARDUINOGFX_PORT_t; +#endif // end !ARM +typedef volatile ARDUINOGFX_PORT_t *PORTreg_t; #if defined(ADAFRUIT_PYPORTAL) || defined(ADAFRUIT_PYBADGE_M4_EXPRESS) || defined(ADAFRUIT_PYGAMER_M4_EXPRESS) || defined(ADAFRUIT_HALLOWING_M4_EXPRESS) #define USE_SPI_DMA ///< Auto DMA if using PyPortal #else - //#define USE_SPI_DMA ///< If set, use DMA if available +//#define USE_SPI_DMA ///< If set, use DMA if available #endif // Another "oops" name -- this now also handles parallel DMA. // If DMA is enabled, Arduino sketch MUST #include @@ -102,6 +105,8 @@ class Arduino_SWSPI : public Arduino_DataBus INLINE void WRITE(uint8_t d); INLINE void WRITE16(uint16_t d); INLINE void WRITE32(uint32_t d); + INLINE void WRITE9BITREPEAT(uint16_t p, uint32_t len); + INLINE void WRITEREPEAT(uint16_t p, uint32_t len); INLINE void CS_HIGH(void); INLINE void CS_LOW(void); INLINE void DC_HIGH(void); @@ -112,7 +117,8 @@ class Arduino_SWSPI : public Arduino_DataBus INLINE void SPI_SCK_LOW(void); INLINE bool SPI_MISO_READ(void); - int8_t _dc, _cs, _sck, _mosi, _miso; + int8_t _dc, _cs; + int8_t _sck, _mosi, _miso; // CLASS INSTANCE VARIABLES --------------------------------------------