Skip to content

Commit 91bec0b

Browse files
Clean up format/comment, remove delay() in stop
stopWaveform may be called from an interrupt (it's called by digitalWrite) so we can't call delay(). Make it a busy wait for the IRQ to clear the waveform. Only set a new timeout of 10us when starting a new waveform when there is no other event coming sooner than that. Update formatting and comments per @devyte's requests. Replace MicrosecondsToCycles() with standard Arduino call.
1 parent ae4f704 commit 91bec0b

File tree

1 file changed

+96
-76
lines changed

1 file changed

+96
-76
lines changed

Diff for: cores/esp8266/core_esp8266_waveform.c

+96-76
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,6 @@
4848
#define SetGPIO(a) do { GPOS = a; } while (0)
4949
#define ClearGPIO(a) do { GPOC = a; } while (0)
5050

51-
// Convert from us to ESP8266 clocks
52-
#define MicrosecondsToCycles(m) (clockCyclesPerMicrosecond() * (m))
53-
5451
// Waveform generator can create tones, PWM, and servos
5552
typedef struct {
5653
uint32_t nextServiceCycle; // ESP cycle timer when a transition required
@@ -59,14 +56,13 @@ typedef struct {
5956
uint32_t nextTimeLowCycles; // Copy over high->low to keep smooth waveform
6057
} Waveform;
6158

62-
// These can be accessed in interrupts, so ensure to bracket access with SEI/CLI
6359
static Waveform waveform[17]; // State of all possible pins
64-
static volatile uint32_t waveformState = 0; // Is the pin high or low
65-
static volatile uint32_t waveformEnabled = 0; // Is it actively running
60+
static volatile uint32_t waveformState = 0; // Is the pin high or low, updated in NMI so no access outside the NMI code
61+
static volatile uint32_t waveformEnabled = 0; // Is it actively running, updated in NMI so no access outside the NMI code
6662

6763
// Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine
68-
static volatile uint32_t waveformToEnable = 0; // Message from startWaveform to IRQ to actuall begin a wvf
69-
static volatile uint32_t waveformToDisable = 0; // Message from startWaveform to IRQ to actuall begin a wvf
64+
static volatile uint32_t waveformToEnable = 0; // Message to the NMI handler to start a waveform on a inactive pin
65+
static volatile uint32_t waveformToDisable = 0; // Message to the NMI handler to disable a pin from waveform generation
7066

7167
static uint32_t (*timer1CB)() = NULL;
7268

@@ -104,7 +100,7 @@ void setTimer1Callback(uint32_t (*fn)()) {
104100
timer1CB = fn;
105101
if (!timerRunning && fn) {
106102
initTimer();
107-
timer1_write(MicrosecondsToCycles(1)); // Cause an interrupt post-haste
103+
timer1_write(microsecondsToClockCycles(1)); // Cause an interrupt post-haste
108104
} else if (timerRunning && !fn && !waveformEnabled) {
109105
deinitTimer();
110106
}
@@ -119,22 +115,27 @@ int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t
119115
}
120116
Waveform *wave = &waveform[pin];
121117
// Adjust to shave off some of the IRQ time, approximately
122-
wave->nextTimeHighCycles = MicrosecondsToCycles(timeHighUS);
123-
wave->nextTimeLowCycles = MicrosecondsToCycles(timeLowUS);
124-
wave->expiryCycle = runTimeUS ? GetCycleCount() + MicrosecondsToCycles(runTimeUS) : 0;
118+
wave->nextTimeHighCycles = microsecondsToClockCycles(timeHighUS);
119+
wave->nextTimeLowCycles = microsecondsToClockCycles(timeLowUS);
120+
wave->expiryCycle = runTimeUS ? GetCycleCount() + microsecondsToClockCycles(runTimeUS) : 0;
125121
if (runTimeUS && !wave->expiryCycle) {
126122
wave->expiryCycle = 1; // expiryCycle==0 means no timeout, so avoid setting it
127123
}
128124

129125
uint32_t mask = 1<<pin;
130126
if (!(waveformEnabled & mask)) {
131127
// Actually set the pin high or low in the IRQ service to guarantee times
132-
wave->nextServiceCycle = GetCycleCount() + MicrosecondsToCycles(1);
128+
wave->nextServiceCycle = GetCycleCount() + microsecondsToClockCycles(1);
133129
waveformToEnable |= mask;
134130
if (!timerRunning) {
135131
initTimer();
132+
timer1_write(microsecondsToClockCycles(10));
133+
} else {
134+
// Ensure timely service....
135+
if (T1L > microsecondsToClockCycles(10)) {
136+
timer1_write(microsecondsToClockCycles(10));
137+
}
136138
}
137-
timer1_write(MicrosecondsToCycles(10)); // Cause an interrupt post-haste
138139
while (waveformToEnable) {
139140
delay(0); // Wait for waveform to update
140141
}
@@ -172,106 +173,125 @@ int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
172173
// If they send >=32, then the shift will result in 0 and it will also return false
173174
uint32_t mask = 1<<pin;
174175
if (!(waveformEnabled & mask)) {
175-
return false; //It's not running, nothing to do here
176+
return false; // It's not running, nothing to do here
176177
}
177178
waveformToDisable |= mask;
178-
// Ensure tiomely service....
179-
if (T1L > MicrosecondsToCycles(10)) {
180-
timer1_write(MicrosecondsToCycles(10));
179+
// Ensure timely service....
180+
if (T1L > microsecondsToClockCycles(10)) {
181+
timer1_write(microsecondsToClockCycles(10));
181182
}
182183
while (waveformToDisable) {
183-
delay(1); // Wait for IRQ to update
184+
/* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
184185
}
185186
if (!waveformEnabled && !timer1CB) {
186187
deinitTimer();
187188
}
188189
return true;
189190
}
190191

192+
// The SDK and hardware take some time to actually get to our NMI code, so
193+
// decrement the next IRQ's timer value by a bit so we can actually catch the
194+
// real CPU cycle counter we want for the waveforms.
191195
#if F_CPU == 80000000
192-
#define DELTAIRQ (MicrosecondsToCycles(3))
196+
#define DELTAIRQ (microsecondsToClockCycles(3))
193197
#else
194-
#define DELTAIRQ (MicrosecondsToCycles(2))
198+
#define DELTAIRQ (microsecondsToClockCycles(2))
195199
#endif
196200

197-
static int startPin = 0;
198-
static int endPin = 0;
199201

200202
static ICACHE_RAM_ATTR void timer1Interrupt() {
201-
uint32_t nextEventCycles = MicrosecondsToCycles(MAXIRQUS);
202-
uint32_t timeoutCycle = GetCycleCountIRQ() + MicrosecondsToCycles(14);
203+
// Optimize the NMI inner loop by keeping track of the min and max GPIO that we
204+
// are generating. In the common case (1 PWM) these may be the same pin and
205+
// we can avoid looking at the other pins.
206+
static int startPin = 0;
207+
static int endPin = 0;
208+
209+
uint32_t nextEventCycles = microsecondsToClockCycles(MAXIRQUS);
210+
uint32_t timeoutCycle = GetCycleCountIRQ() + microsecondsToClockCycles(14);
203211

204212
if (waveformToEnable || waveformToDisable) {
205213
// Handle enable/disable requests from main app.
206214
waveformEnabled = (waveformEnabled & ~waveformToDisable) | waveformToEnable; // Set the requested waveforms on/off
207215
waveformState &= ~waveformToEnable; // And clear the state of any just started
208216
waveformToEnable = 0;
209217
waveformToDisable = 0;
218+
// Find the first GPIO being generated by checking GCC's find-first-set (returns 1 + the bit of the first 1 in an int32_t)
210219
startPin = __builtin_ffs(waveformEnabled) - 1;
220+
// Find the last bit by subtracting off GCC's count-leading-zeros (no offset in this one)
211221
endPin = 32 - __builtin_clz(waveformEnabled);
212222
}
213223

214224
bool done = false;
215-
if (waveformEnabled) do {
216-
nextEventCycles = MicrosecondsToCycles(MAXIRQUS);
217-
for (int i = startPin; i <= endPin; i++) {
218-
uint32_t mask = 1<<i;
219-
220-
// If it's not on, ignore!
221-
if (!(waveformEnabled & mask)) {
222-
continue;
223-
}
225+
if (waveformEnabled) {
226+
do {
227+
nextEventCycles = microsecondsToClockCycles(MAXIRQUS);
228+
for (int i = startPin; i <= endPin; i++) {
229+
uint32_t mask = 1<<i;
230+
231+
// If it's not on, ignore!
232+
if (!(waveformEnabled & mask)) {
233+
continue;
234+
}
224235

225-
Waveform *wave = &waveform[i];
226-
uint32_t now = GetCycleCountIRQ();
236+
Waveform *wave = &waveform[i];
237+
uint32_t now = GetCycleCountIRQ();
238+
239+
// Disable any waveforms that are done
240+
if (wave->expiryCycle) {
241+
int32_t expiryToGo = wave->expiryCycle - now;
242+
if (expiryToGo < 0) {
243+
// Done, remove!
244+
waveformEnabled &= ~mask;
245+
if (i == 16) {
246+
GP16O &= ~1;
247+
} else {
248+
ClearGPIO(mask);
249+
}
250+
continue;
251+
}
252+
}
227253

228-
// Disable any waveforms that are done
229-
if (wave->expiryCycle) {
230-
int32_t expiryToGo = wave->expiryCycle - now;
231-
if (expiryToGo < 0) {
232-
// Done, remove!
233-
waveformEnabled &= ~mask;
234-
if (i==16) GP16O &= ~1;
235-
else ClearGPIO(mask);
236-
continue;
254+
// Check for toggles
255+
int32_t cyclesToGo = wave->nextServiceCycle - now;
256+
if (cyclesToGo < 0) {
257+
waveformState ^= mask;
258+
if (waveformState & mask) {
259+
if (i == 16) {
260+
GP16O |= 1; // GPIO16 write slow as it's RMW
261+
} else {
262+
SetGPIO(mask);
263+
}
264+
wave->nextServiceCycle = now + wave->nextTimeHighCycles;
265+
nextEventCycles = min_u32(nextEventCycles, wave->nextTimeHighCycles);
266+
} else {
267+
if (i == 16) {
268+
GP16O &= ~1; // GPIO16 write slow as it's RMW
269+
} else {
270+
ClearGPIO(mask);
271+
}
272+
wave->nextServiceCycle = now + wave->nextTimeLowCycles;
273+
nextEventCycles = min_u32(nextEventCycles, wave->nextTimeLowCycles);
237274
}
238-
}
239-
240-
// Check for toggles
241-
int32_t cyclesToGo = wave->nextServiceCycle - now;
242-
if (cyclesToGo < 0) {
243-
waveformState ^= mask;
244-
if (waveformState & mask) {
245-
if (i==16) GP16O |= 1; // GPIO16 write slow as it's RMW
246-
else SetGPIO(mask);
247-
248-
wave->nextServiceCycle = now + wave->nextTimeHighCycles;
249-
nextEventCycles = min_u32(nextEventCycles, wave->nextTimeHighCycles);
250275
} else {
251-
if (i==16) GP16O &= ~1; // GPIO16 write slow as it's RMW
252-
else ClearGPIO(mask);
253-
254-
wave->nextServiceCycle = now + wave->nextTimeLowCycles;
255-
nextEventCycles = min_u32(nextEventCycles, wave->nextTimeLowCycles);
276+
uint32_t deltaCycles = wave->nextServiceCycle - now;
277+
nextEventCycles = min_u32(nextEventCycles, deltaCycles);
256278
}
257-
} else {
258-
uint32_t deltaCycles = wave->nextServiceCycle - now;
259-
nextEventCycles = min_u32(nextEventCycles, deltaCycles);
260279
}
261-
}
262-
uint32_t now = GetCycleCountIRQ();
263-
done = false;
264-
int32_t cycleDeltaNextEvent = timeoutCycle - (now + nextEventCycles);
265-
if (cycleDeltaNextEvent < 0) done = true;
266-
int32_t cyclesLeftTimeout = timeoutCycle - now;
267-
if (cyclesLeftTimeout < 0) done = true;
268-
} while (!done);
280+
281+
// Exit the loop if we've hit the fixed runtime limit or the next event is known to be after that timeout would occur
282+
uint32_t now = GetCycleCountIRQ();
283+
int32_t cycleDeltaNextEvent = timeoutCycle - (now + nextEventCycles);
284+
int32_t cyclesLeftTimeout = timeoutCycle - now;
285+
done = (cycleDeltaNextEvent < 0) || (cyclesLeftTimeout < 0);
286+
} while (!done);
287+
} // if (waveformEnabled)
269288

270289
if (timer1CB) {
271290
nextEventCycles = min_u32(nextEventCycles, timer1CB());
272291
}
273-
if (nextEventCycles < MicrosecondsToCycles(10)) {
274-
nextEventCycles = MicrosecondsToCycles(10);
292+
293+
if (nextEventCycles < microsecondsToClockCycles(10)) {
294+
nextEventCycles = microsecondsToClockCycles(10);
275295
}
276296
nextEventCycles -= DELTAIRQ;
277297

@@ -281,5 +301,5 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
281301
#else
282302
T1L = nextEventCycles; // Already know we're in range by MAXIRQUS
283303
#endif
284-
TEIE |= TEIE1; //edge int enable
304+
TEIE |= TEIE1; // Edge int enable
285305
}

0 commit comments

Comments
 (0)