diff --git a/DMD2.cpp b/DMD2.cpp index 9fb977b..045408e 100644 --- a/DMD2.cpp +++ b/DMD2.cpp @@ -23,8 +23,10 @@ typedef intptr_t port_reg_t; SPIDMD::SPIDMD(byte panelsWide, byte panelsHigh) -#ifdef ESP8266 - : BaseDMD(panelsWide, panelsHigh, 15, 16, 12, 0) +#if defined(ESP8266) + : BaseDMD(panelsWide, panelsHigh, 0, 16, 12, 15) +#elif defined(ESP32) + : BaseDMD(panelsWide, panelsHigh, 14, 27, 26, 25) #else : BaseDMD(panelsWide, panelsHigh, 9, 6, 7, 8) #endif @@ -45,7 +47,7 @@ void SPIDMD::beginNoTimer() SPI.setDataMode(SPI_MODE0); // CPOL=0, CPHA=0 #ifdef __AVR__ SPI.setClockDivider(SPI_CLOCK_DIV4); // 4MHz clock. 8MHz (DIV2 not DIV4) is possible if you have short cables. Longer cables may need DIV8/DIV16. -#elif defined(ESP8266) +#elif defined(ESP8266) || defined(ESP32) SPI.setFrequency(4000000); // ESP can run at 80mhz or 160mhz, setting frequency directly is easier, set to 4MHz. #else SPI.setClockDivider(20); // 4.2MHz on Due. Same comment as above applies (lower numbers = less divider = faster speeds.) @@ -81,7 +83,12 @@ void BaseDMD::scanDisplay() writeSPIData(rows, rowsize); +#if defined(ESP32) + ledcWrite(ESP32_PIN_NOE_PWM_CHANNEL, 0); +#else digitalWrite(pin_noe, LOW); +#endif + digitalWrite(pin_sck, HIGH); // Latch DMD shift register output digitalWrite(pin_sck, LOW); // (Deliberately left as digitalWrite to ensure decent latching time) @@ -96,14 +103,18 @@ void BaseDMD::scanDisplay() scan_row = (scan_row + 1) % 4; // Output enable pin is either fixed on, or PWMed for a variable brightness display +#if defined(ESP32) + ledcWrite(ESP32_PIN_NOE_PWM_CHANNEL, brightness); +#else if(brightness == 255) digitalWrite(pin_noe, HIGH); else analogWrite(pin_noe, brightness); +#endif } -#ifdef ESP8266 -// No SoftDMD for ESP8266 for now +#if defined(ESP8266) || defined(ESP32) +// No SoftDMD for ESP8266 or ESP32 for now #else SoftDMD::SoftDMD(byte panelsWide, byte panelsHigh) : BaseDMD(panelsWide, panelsHigh, 9, 6, 7, 8), @@ -171,8 +182,10 @@ BaseDMD::BaseDMD(byte panelsWide, byte panelsHigh, byte pin_noe, byte pin_a, byt pin_a(pin_a), pin_b(pin_b), pin_sck(pin_sck), -#ifdef ESP8266 - default_pins(pin_noe == 15 && pin_a == 16 && pin_b == 12 && pin_sck == 0), +#if defined(ESP8266) + default_pins(pin_noe == 0 && pin_a == 16 && pin_b == 12 && pin_sck == 15), +#elif defined(ESP32) + default_pins(pin_noe == 14 && pin_a == 27 && pin_b == 26 && pin_sck == 25), #else default_pins(pin_noe == 9 && pin_a == 6 && pin_b == 7 && pin_sck == 8), #endif @@ -183,8 +196,17 @@ BaseDMD::BaseDMD(byte panelsWide, byte panelsHigh, byte pin_noe, byte pin_a, byt void BaseDMD::beginNoTimer() { +#if defined(ESP32) + pinMode(pin_noe, OUTPUT); + // configure LED PWM functionalitites + ledcSetup(ESP32_PIN_NOE_PWM_CHANNEL, ESP32_PWM_FREQ, ESP32_PWM_RES); + + // attach pin_noe to pwm channel + ledcAttachPin(pin_noe, ESP32_PIN_NOE_PWM_CHANNEL); +#else digitalWrite(pin_noe, LOW); pinMode(pin_noe, OUTPUT); +#endif digitalWrite(pin_a, LOW); pinMode(pin_a, OUTPUT); diff --git a/DMD2.h b/DMD2.h index 96bbd50..26fcaf8 100644 --- a/DMD2.h +++ b/DMD2.h @@ -25,8 +25,21 @@ #ifndef DMD2_H #define DMD2_H +// ESP32 doesn't define byte datatype +#ifndef byte +#define byte uint8_t +#endif + +#include "Arduino.h" #include "Print.h" #include "SPI.h" +#include "DMD2.h" + +#if defined(ESP32) +#define ESP32_PIN_NOE_PWM_CHANNEL 0 +#define ESP32_PWM_FREQ 5000 +#define ESP32_PWM_RES 8 +#endif // Dimensions of a single display const unsigned int PANEL_WIDTH = 32; @@ -140,7 +153,7 @@ class DMDFrame void drawString(int x, int y, const char *bChars, DMDGraphicsMode mode=GRAPHICS_ON, const uint8_t *font = NULL); void drawString(int x, int y, const String &str, DMDGraphicsMode mode=GRAPHICS_ON, const uint8_t *font = NULL); -#if defined(__AVR__) || defined(ESP8266) +#if defined(__AVR__) || defined(ESP8266) || defined(ESP32) void drawString_P(int x, int y, const char *flashStr, DMDGraphicsMode mode=GRAPHICS_ON, const uint8_t *font = NULL); inline void drawString(int x, int y, const __FlashStringHelper *flashStr, DMDGraphicsMode mode=GRAPHICS_ON, const uint8_t *font = NULL) { return drawString_P(x,y,(const char*)flashStr, mode, font); @@ -151,7 +164,7 @@ class DMDFrame int charWidth(const char letter, const uint8_t *font = NULL); //Find the width of a string (width of all characters plus 1 pixel "kerning" between each character) -#if defined(__AVR__) || defined(ESP8266) +#if defined(__AVR__) || defined(ESP8266) || defined(ESP32) unsigned int stringWidth_P(const char *flashStr, const uint8_t *font = NULL); inline unsigned int stringWidth(const __FlashStringHelper *flashStr, const uint8_t *font = NULL) { return stringWidth_P((const char*)flashStr, font); @@ -261,8 +274,8 @@ class SPIDMD : public BaseDMD void writeSPIData(volatile uint8_t *rows[4], const int rowsize); }; -#ifdef ESP8266 -// No SoftDMD for ESP8266 for now +#if defined(ESP8266) || defined(ESP32) +// No SoftDMD for ESP8266 or ESP32 for now #else class SoftDMD : public BaseDMD { diff --git a/DMD2_Text.cpp b/DMD2_Text.cpp index 331e29b..4a1f03b 100644 --- a/DMD2_Text.cpp +++ b/DMD2_Text.cpp @@ -153,7 +153,7 @@ template __attribute__((always_inline)) inline unsigned int _str } -#if defined(__AVR__) || defined (ESP8266) +#if defined(__AVR__) || defined (ESP8266) || defined(ESP32) // Small wrapper class to allow indexing of progmem strings via [] (should be inlined out of the actual implementation) class _FlashStringWrapper { const char *str; diff --git a/DMD2_Timer.cpp b/DMD2_Timer.cpp index 2662e80..baeb1cc 100644 --- a/DMD2_Timer.cpp +++ b/DMD2_Timer.cpp @@ -35,6 +35,13 @@ #define ESP8266_TIMER0_TICKS microsecondsToClockCycles(250) // 250 microseconds between calls to scan_running_dmds seems to works better than 1000. +// Assuming esp32 using frequency of 80MHz... +#define ESP32_TIMER0 0 +// run counter to increment every 1 microseconds +#define ESP32_TIMER0_PRESCALER 80 +// call scan_running_dmds every 250 microseconds on ESP32 +#define ESP32_TIMER0_RELOAD_ON 250 + #ifdef NO_TIMERS // Timer-free stub code which gets compiled in only if NO_TIMERS is set @@ -159,6 +166,50 @@ void BaseDMD::end() scanDisplay(); } +#elif defined(ESP32) +hw_timer_t *timer = NULL; +portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; + +void IRAM_ATTR esp32_ISR_wrapper(){ + // claim interrupt handling + portENTER_CRITICAL_ISR(&timerMux); + + scan_running_dmds(); + + // end interrupt handling + portEXIT_CRITICAL_ISR(&timerMux); +} + +void BaseDMD::begin() +{ + beginNoTimer(); + + timer = timerBegin(ESP32_TIMER0, ESP32_TIMER0_PRESCALER, true); + + register_running_dmd(this); + + timerAttachInterrupt(timer, &esp32_ISR_wrapper, true); + timerAlarmWrite(timer, ESP32_TIMER0_RELOAD_ON, true); + + // safety net + yield(); + timerAlarmEnable(timer); +} + +void BaseDMD::end() +{ + bool still_running = unregister_running_dmd(this); + if(!still_running) + { + timerAlarmDisable(timer); + timerDetachInterrupt(timer); + timerEnd(timer); + timer = NULL; + } + clearScreen(); + scanDisplay(); +} + #endif // ifdef __AVR__ /* Following functions are static non-architecture-specific functions diff --git a/library.properties b/library.properties index 00bc7be..067f425 100644 --- a/library.properties +++ b/library.properties @@ -6,4 +6,4 @@ sentence=Updated (beta) library for Freetronics DMD dot matrix displays. paragraph=Supports graphics operations (test, images, etc.) Still experimental, the stable library is called called "DMD" category=Display url=https://github.com/freetronics/DMD2/ -architectures=avr,sam,esp8266 +architectures=avr,sam,esp8266,esp32