Skip to content

Commit be7a732

Browse files
earlephilhowerdevyte
authored andcommitted
Compatibility and IRQ fixed for waveform/tone/pwm (#4872)
* Compatibility and IRQ fixed for waveform/tone/pwm Fix a compiler ambiguity introduced with a floating point frequency option for tone(). Thanks to @Rob58329 for discovering this and proposing the fix. Match original analogWrite behavior by going from 0...1023 (PWMRANGE) and not 0...1024, and also explicitly set the analogWrite pin to an OUTPUT. Thanks to @JAndrassy for finding this. Fixes #4380 discovered by @cranphin where interrupts were disabled on a stopWaveform(). Remove that completely and bracket the update of non-atomic fields in the structure with disable/enable IRQs for safety. * Fix tone(int,int,int) infinite loop Explicitly cast the frequency, when passed in as an int, to an unsigned int. Verified with snippet: tone(D1, (int)1000, 500); tone(D1, (unsigned int)1000, 500); tone(D1, 1000.0, 500); tone(D1, (int)1000); tone(D1, (unsigned int)1000); tone(D1, 1000.0);
1 parent 1eb0645 commit be7a732

File tree

4 files changed

+22
-6
lines changed

4 files changed

+22
-6
lines changed

cores/esp8266/Arduino.h

+1
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 100000
279279
unsigned long pulseInLong(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L);
280280

281281
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0);
282+
void tone(uint8_t _pin, int frequency, unsigned long duration = 0);
282283
void tone(uint8_t _pin, double frequency, unsigned long duration = 0);
283284
void noTone(uint8_t _pin);
284285

cores/esp8266/Tone.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ void tone(uint8_t _pin, double frequency, unsigned long duration) {
7070
}
7171

7272

73+
// Fix ambiguous tone() binding when adding in a duration
74+
void tone(uint8_t _pin, int frequency, unsigned long duration) {
75+
// Call the unsigned int version of the function explicitly
76+
tone(_pin, (unsigned int)frequency, duration);
77+
}
78+
79+
7380
void noTone(uint8_t _pin) {
7481
if (_pin > 16) {
7582
return;

cores/esp8266/core_esp8266_waveform.c

+12-5
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,6 @@
4343
// Need speed, not size, here
4444
#pragma GCC optimize ("O3")
4545

46-
// Map the IRQ stuff to standard terminology
47-
#define cli() ets_intr_lock()
48-
#define sei() ets_intr_unlock()
49-
5046
// Maximum delay between IRQs
5147
#define MAXIRQUS (10000)
5248

@@ -180,6 +176,12 @@ int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t
180176
if (!wave) {
181177
return false;
182178
}
179+
180+
// To safely update the packed bitfields we need to stop interrupts while setting them as we could
181+
// get an IRQ in the middle of a multi-instruction mask-and-set required to change them which would
182+
// then cause an IRQ update of these values (.enabled only, for now) to be lost.
183+
ets_intr_lock();
184+
183185
wave->nextTimeHighCycles = MicrosecondsToCycles(timeHighUS) - 70; // Take out some time for IRQ codepath
184186
wave->nextTimeLowCycles = MicrosecondsToCycles(timeLowUS) - 70; // Take out some time for IRQ codepath
185187
wave->timeLeftCycles = MicrosecondsToCycles(runTimeUS);
@@ -193,13 +195,19 @@ int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t
193195
}
194196
ReloadTimer(MicrosecondsToCycles(1)); // Cause an interrupt post-haste
195197
}
198+
199+
// Re-enable interrupts here since we're done with the update
200+
ets_intr_unlock();
201+
196202
return true;
197203
}
198204

199205
// Stops a waveform on a pin
200206
int stopWaveform(uint8_t pin) {
201207
for (size_t i = 0; i < countof(waveform); i++) {
202208
if (((pin == 16) && waveform[i].gpio16Mask) || ((pin != 16) && (waveform[i].gpioMask == 1<<pin))) {
209+
// Note that there is no interrupt unsafety here. The IRQ can only ever change .enabled from 1->0
210+
// We're also doing that, so even if an IRQ occurred it would still stay as 0.
203211
waveform[i].enabled = 0;
204212
int cnt = timer1CB?1:0;
205213
for (size_t i = 0; i < countof(waveform); i++) {
@@ -211,7 +219,6 @@ int stopWaveform(uint8_t pin) {
211219
return true;
212220
}
213221
}
214-
cli();
215222
return false;
216223
}
217224

cores/esp8266/core_esp8266_wiring_pwm.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727

2828
static uint32_t analogMap = 0;
29-
static int32_t analogScale = 255;
29+
static int32_t analogScale = PWMRANGE;
3030
static uint16_t analogFreq = 1000;
3131

3232
extern void __analogWriteRange(uint32_t range) {
@@ -59,6 +59,7 @@ extern void __analogWrite(uint8_t pin, int val) {
5959
analogMap &= ~(1 << pin);
6060
uint32_t high = (analogPeriod * val) / analogScale;
6161
uint32_t low = analogPeriod - high;
62+
pinMode(pin, OUTPUT);
6263
if (low == 0) {
6364
stopWaveform(pin);
6465
digitalWrite(pin, HIGH);

0 commit comments

Comments
 (0)