From 8f928122c773d438a33d12dd221c14a3d652f121 Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Sun, 12 Apr 2020 18:03:12 -0700 Subject: [PATCH 1/2] Allow waveforms to be specified in clock cycles Allow the PWM to specify sub-microsecond waveform edges, as have been proposed by @dok-net and me. No other changes intended. This will increase the linearity at 30 and 40 kHZ PWM rates, but leave most other things unaffected. --- cores/esp8266/core_esp8266_waveform.cpp | 14 +++++++++----- cores/esp8266/core_esp8266_waveform.h | 2 ++ cores/esp8266/core_esp8266_wiring_pwm.cpp | 5 +++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/cores/esp8266/core_esp8266_waveform.cpp b/cores/esp8266/core_esp8266_waveform.cpp index 59fce8695e..e3924cf08a 100644 --- a/cores/esp8266/core_esp8266_waveform.cpp +++ b/cores/esp8266/core_esp8266_waveform.cpp @@ -112,15 +112,19 @@ void setTimer1Callback(uint32_t (*fn)()) { // waveform smoothly on next low->high transition. For immediate change, stopWaveform() // first, then it will immediately begin. int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) { - if ((pin > 16) || isFlashInterfacePin(pin)) { + return startWaveformCycles(pin, microsecondsToClockCycles(timeHighUS), microsecondsToClockCycles(timeLowUS), microsecondsToClockCycles(runTimeUS)); +} + +int startWaveformCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles) { + if ((pin > 16) || isFlashInterfacePin(pin)) { return false; } Waveform *wave = &waveform[pin]; // Adjust to shave off some of the IRQ time, approximately - wave->nextTimeHighCycles = microsecondsToClockCycles(timeHighUS); - wave->nextTimeLowCycles = microsecondsToClockCycles(timeLowUS); - wave->expiryCycle = runTimeUS ? GetCycleCount() + microsecondsToClockCycles(runTimeUS) : 0; - if (runTimeUS && !wave->expiryCycle) { + wave->nextTimeHighCycles = timeHighCycles; + wave->nextTimeLowCycles = timeLowCycles; + wave->expiryCycle = runTimeCycles ? GetCycleCount() + runTimeCycles : 0; + if (runTimeCycles && !wave->expiryCycle) { wave->expiryCycle = 1; // expiryCycle==0 means no timeout, so avoid setting it } diff --git a/cores/esp8266/core_esp8266_waveform.h b/cores/esp8266/core_esp8266_waveform.h index 24ce91fb36..9a5f2ce372 100644 --- a/cores/esp8266/core_esp8266_waveform.h +++ b/cores/esp8266/core_esp8266_waveform.h @@ -50,6 +50,8 @@ extern "C" { // If runtimeUS > 0 then automatically stop it after that many usecs. // Returns true or false on success or failure. int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS); +// Same as above, but pass in CPU clock cycles instead of microseconds +int startWaveformCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles); // Stop a waveform, if any, on the specified pin. // Returns true or false on success or failure. int stopWaveform(uint8_t pin); diff --git a/cores/esp8266/core_esp8266_wiring_pwm.cpp b/cores/esp8266/core_esp8266_wiring_pwm.cpp index 5a3481bbd8..b15b95f828 100644 --- a/cores/esp8266/core_esp8266_wiring_pwm.cpp +++ b/cores/esp8266/core_esp8266_wiring_pwm.cpp @@ -25,6 +25,7 @@ #include "core_esp8266_waveform.h" extern "C" { +#include "user_interface.h" static uint32_t analogMap = 0; static int32_t analogScale = PWMRANGE; @@ -50,7 +51,7 @@ extern void __analogWrite(uint8_t pin, int val) { if (pin > 16) { return; } - uint32_t analogPeriod = 1000000L / analogFreq; + uint32_t analogPeriod = (1000000L * system_get_cpu_freq()) / analogFreq; if (val < 0) { val = 0; } else if (val > analogScale) { @@ -68,7 +69,7 @@ extern void __analogWrite(uint8_t pin, int val) { stopWaveform(pin); digitalWrite(pin, LOW); } else { - if (startWaveform(pin, high, low, 0)) { + if (startWaveformCycles(pin, high, low, 0)) { analogMap |= (1 << pin); } } From 6001e1df4c76cc2ec4abc01a1fb684a4a74a0bde Mon Sep 17 00:00:00 2001 From: "Earle F. Philhower, III" Date: Sun, 12 Apr 2020 21:34:57 -0700 Subject: [PATCH 2/2] Cycle-accurate wafveform to specify Tone periods --- cores/esp8266/Tone.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cores/esp8266/Tone.cpp b/cores/esp8266/Tone.cpp index 0a156fe213..ff895f153f 100644 --- a/cores/esp8266/Tone.cpp +++ b/cores/esp8266/Tone.cpp @@ -22,6 +22,7 @@ */ #include "Arduino.h" +#include "user_interface.h" #include "core_esp8266_waveform.h" // Which pins have a tone running on them? @@ -35,10 +36,10 @@ static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, unsigned long pinMode(_pin, OUTPUT); - high = std::max(high, (uint32_t)25); // new 20KHz maximum tone frequency, - low = std::max(low, (uint32_t)25); // (25us high + 25us low period = 20KHz) + high = std::max(high, (uint32_t)microsecondsToClockCycles(25)); // new 20KHz maximum tone frequency, + low = std::max(low, (uint32_t)microsecondsToClockCycles(25)); // (25us high + 25us low period = 20KHz) - if (startWaveform(_pin, high, low, (uint32_t) duration * 1000)) { + if (startWaveformCycles(_pin, high, low, microsecondsToClockCycles(duration * 1000))) { _toneMap |= 1 << _pin; } } @@ -48,7 +49,7 @@ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) { if (frequency == 0) { noTone(_pin); } else { - uint32_t period = 1000000L / frequency; + uint32_t period = (1000000L * system_get_cpu_freq()) / frequency; uint32_t high = period / 2; uint32_t low = period - high; _startTone(_pin, high, low, duration); @@ -62,7 +63,7 @@ void tone(uint8_t _pin, double frequency, unsigned long duration) { if (frequency < 1.0) { // FP means no exact comparisons noTone(_pin); } else { - double period = 1000000.0 / frequency; + double period = (1000000.0L * system_get_cpu_freq()) / frequency; uint32_t high = (uint32_t)((period / 2.0) + 0.5); uint32_t low = (uint32_t)(period + 0.5) - high; _startTone(_pin, high, low, duration);