diff --git a/cores/esp8266/Tone.cpp b/cores/esp8266/Tone.cpp index ff895f153f..064fdad5df 100644 --- a/cores/esp8266/Tone.cpp +++ b/cores/esp8266/Tone.cpp @@ -22,14 +22,14 @@ */ #include "Arduino.h" -#include "user_interface.h" #include "core_esp8266_waveform.h" +#include "user_interface.h" // Which pins have a tone running on them? static uint32_t _toneMap = 0; -static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, unsigned long duration) { +static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, uint32_t duration) { if (_pin > 16) { return; } @@ -39,7 +39,10 @@ static void _startTone(uint8_t _pin, uint32_t high, uint32_t low, unsigned long 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 (startWaveformCycles(_pin, high, low, microsecondsToClockCycles(duration * 1000))) { + duration = microsecondsToClockCycles(duration * 1000UL); + duration += high + low - 1; + duration -= duration % (high + low); + if (startWaveformClockCycles(_pin, high, low, duration)) { _toneMap |= 1 << _pin; } } @@ -49,7 +52,7 @@ void tone(uint8_t _pin, unsigned int frequency, unsigned long duration) { if (frequency == 0) { noTone(_pin); } else { - uint32_t period = (1000000L * system_get_cpu_freq()) / frequency; + uint32_t period = microsecondsToClockCycles(1000000UL) / frequency; uint32_t high = period / 2; uint32_t low = period - high; _startTone(_pin, high, low, duration); @@ -63,7 +66,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.0L * system_get_cpu_freq()) / frequency; + double period = (double)microsecondsToClockCycles(1000000UL) / 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); diff --git a/cores/esp8266/core_esp8266_waveform.cpp b/cores/esp8266/core_esp8266_waveform.cpp index e3924cf08a..bd9667cf72 100644 --- a/cores/esp8266/core_esp8266_waveform.cpp +++ b/cores/esp8266/core_esp8266_waveform.cpp @@ -5,13 +5,13 @@ Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. The core idea is to have a programmable waveform generator with a unique - high and low period (defined in microseconds). TIMER1 is set to 1-shot - mode and is always loaded with the time until the next edge of any live - waveforms. + high and low period (defined in microseconds or CPU clock cycles). TIMER1 is + set to 1-shot mode and is always loaded with the time until the next edge + of any live waveforms. Up to one waveform generator per pin supported. - Each waveform generator is synchronized to the ESP cycle counter, not the + Each waveform generator is synchronized to the ESP clock cycle counter, not the timer. This allows for removing interrupt jitter and delay as the counter always increments once per 80MHz clock. Changes to a waveform are contiguous and only take effect on the next waveform transition, @@ -19,8 +19,9 @@ This replaces older tone(), analogWrite(), and the Servo classes. - Everywhere in the code where "cycles" is used, it means ESP.getCycleTime() - cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz). + Everywhere in the code where "cycles" is used, it means ESP.getCycleCount() + clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1 + cycles (which may be 2 CPU clock cycles @ 160MHz). This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -112,10 +113,10 @@ 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) { - return startWaveformCycles(pin, microsecondsToClockCycles(timeHighUS), microsecondsToClockCycles(timeLowUS), microsecondsToClockCycles(runTimeUS)); + return startWaveformClockCycles(pin, microsecondsToClockCycles(timeHighUS), microsecondsToClockCycles(timeLowUS), microsecondsToClockCycles(runTimeUS)); } -int startWaveformCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles) { +int startWaveformClockCycles(uint8_t pin, uint32_t timeHighCycles, uint32_t timeLowCycles, uint32_t runTimeCycles) { if ((pin > 16) || isFlashInterfacePin(pin)) { return false; } diff --git a/cores/esp8266/core_esp8266_waveform.h b/cores/esp8266/core_esp8266_waveform.h index 9a5f2ce372..e42a17f89f 100644 --- a/cores/esp8266/core_esp8266_waveform.h +++ b/cores/esp8266/core_esp8266_waveform.h @@ -5,13 +5,13 @@ Copyright (c) 2018 Earle F. Philhower, III. All rights reserved. The core idea is to have a programmable waveform generator with a unique - high and low period (defined in microseconds). TIMER1 is set to 1-shot - mode and is always loaded with the time until the next edge of any live - waveforms. + high and low period (defined in microseconds or CPU clock cycles). TIMER1 is + set to 1-shot mode and is always loaded with the time until the next edge + of any live waveforms. Up to one waveform generator per pin supported. - Each waveform generator is synchronized to the ESP cycle counter, not the + Each waveform generator is synchronized to the ESP clock cycle counter, not the timer. This allows for removing interrupt jitter and delay as the counter always increments once per 80MHz clock. Changes to a waveform are contiguous and only take effect on the next waveform transition, @@ -19,8 +19,9 @@ This replaces older tone(), analogWrite(), and the Servo classes. - Everywhere in the code where "cycles" is used, it means ESP.getCycleTime() - cycles, not TIMER1 cycles (which may be 2 CPU clocks @ 160MHz). + Everywhere in the code where "cycles" is used, it means ESP.getCycleCount() + clock cycle count, or an interval measured in CPU clock cycles, but not TIMER1 + cycles (which may be 2 CPU clock cycles @ 160MHz). This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -50,8 +51,10 @@ 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); +// Start or change a waveform of the specified high and low CPU clock cycles on specific pin. +// If runtimeCycles > 0 then automatically stop it after that many CPU clock cycles. +// Returns true or false on success or failure. +int startWaveformClockCycles(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 b15b95f828..1ebdd64c89 100644 --- a/cores/esp8266/core_esp8266_wiring_pwm.cpp +++ b/cores/esp8266/core_esp8266_wiring_pwm.cpp @@ -25,7 +25,6 @@ #include "core_esp8266_waveform.h" extern "C" { -#include "user_interface.h" static uint32_t analogMap = 0; static int32_t analogScale = PWMRANGE; @@ -51,7 +50,7 @@ extern void __analogWrite(uint8_t pin, int val) { if (pin > 16) { return; } - uint32_t analogPeriod = (1000000L * system_get_cpu_freq()) / analogFreq; + uint32_t analogPeriod = microsecondsToClockCycles(1000000UL) / analogFreq; if (val < 0) { val = 0; } else if (val > analogScale) { @@ -63,13 +62,11 @@ extern void __analogWrite(uint8_t pin, int val) { uint32_t low = analogPeriod - high; pinMode(pin, OUTPUT); if (low == 0) { - stopWaveform(pin); digitalWrite(pin, HIGH); } else if (high == 0) { - stopWaveform(pin); digitalWrite(pin, LOW); } else { - if (startWaveformCycles(pin, high, low, 0)) { + if (startWaveformClockCycles(pin, high, low, 0)) { analogMap |= (1 << pin); } } diff --git a/libraries/Servo/src/Servo.cpp b/libraries/Servo/src/Servo.cpp index 57294102b5..3dc2c7753e 100644 --- a/libraries/Servo/src/Servo.cpp +++ b/libraries/Servo/src/Servo.cpp @@ -24,6 +24,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #include #include "core_esp8266_waveform.h" +uint32_t Servo::_servoMap = 0; + // similiar to map but will have increased accuracy that provides a more // symetric api (call it and use result to reverse will provide the original value) int improved_map(int value, int minIn, int maxIn, int minOut, int maxOut) @@ -82,6 +84,7 @@ uint8_t Servo::attach(int pin, uint16_t minUs, uint16_t maxUs) void Servo::detach() { if (_attached) { + _servoMap &= ~(1 << _pin); stopWaveform(_pin); _attached = false; digitalWrite(_pin, LOW); @@ -105,7 +108,10 @@ void Servo::writeMicroseconds(int value) { _valueUs = value; if (_attached) { - startWaveform(_pin, _valueUs, REFRESH_INTERVAL - _valueUs, 0); + _servoMap &= ~(1 << _pin); + if (startWaveform(_pin, _valueUs, REFRESH_INTERVAL - _valueUs, 0)) { + _servoMap |= (1 << _pin); + } } } diff --git a/libraries/Servo/src/Servo.h b/libraries/Servo/src/Servo.h index c9a3811744..45f593c0d8 100644 --- a/libraries/Servo/src/Servo.h +++ b/libraries/Servo/src/Servo.h @@ -72,6 +72,7 @@ class Servo int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release) bool attached(); // return true if this servo is attached, otherwise false private: + static uint32_t _servoMap; bool _attached; uint8_t _pin; uint16_t _minUs;