@@ -90,8 +90,8 @@ namespace {
90
90
volatile uint32_t enabled = 0 ; // Is it actively running, updated in NMI so no access outside the NMI code
91
91
92
92
// Enable lock-free by only allowing updates to waveform.states and waveform.enabled from IRQ service routine
93
- volatile uint32_t toEnable = 0 ; // Message to the NMI handler to start exactly one waveform on a inactive pin
94
- volatile uint32_t toDisable = 0 ; // Message to the NMI handler to disable exactly one pin from waveform generation
93
+ volatile int32_t toSet = - 1 ; // Message to the NMI handler to start/modify exactly one waveform
94
+ volatile int32_t toDisable = - 1 ; // Message to the NMI handler to disable exactly one pin from waveform generation
95
95
96
96
uint32_t (*timer1CB)() = nullptr ;
97
97
@@ -180,7 +180,7 @@ int startWaveformClockCycles(uint8_t pin, uint32_t highCcys, uint32_t lowCcys,
180
180
}
181
181
}
182
182
std::atomic_thread_fence (std::memory_order_release);
183
- waveform.toEnable = 1UL << pin;
183
+ waveform.toSet = pin;
184
184
if (!waveform.timer1Running ) {
185
185
initTimer ();
186
186
timer1_write (microsecondsToClockCycles (1 ));
@@ -189,20 +189,20 @@ int startWaveformClockCycles(uint8_t pin, uint32_t highCcys, uint32_t lowCcys,
189
189
// Must not interfere if Timer is due shortly, cluster phases to reduce interrupt load
190
190
timer1_write (microsecondsToClockCycles (1 ));
191
191
}
192
- while (waveform.toEnable ) {
193
- delay (0 ); // Wait for waveform to update
194
- }
195
192
}
196
193
else {
197
194
wave.mode = WaveformMode::INFINITE; // turn off possible expiry to make update atomic from NMI
198
195
std::atomic_thread_fence (std::memory_order_release);
199
196
wave.expiryCcy = runTimeCcys; // in WaveformMode::UPDATEEXPIRY, temporarily hold relative cycle count
200
- std::atomic_thread_fence (std::memory_order_release);
201
197
if (runTimeCcys) {
202
198
wave.mode = WaveformMode::UPDATEEXPIRY;
203
199
std::atomic_thread_fence (std::memory_order_release);
200
+ waveform.toSet = pin;
204
201
}
205
202
}
203
+ while (waveform.toSet >= 0 ) {
204
+ delay (0 ); // Wait for waveform to update
205
+ }
206
206
return true ;
207
207
}
208
208
@@ -215,12 +215,12 @@ int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
215
215
// If user sends in a pin >16 but <32, this will always point to a 0 bit
216
216
// If they send >=32, then the shift will result in 0 and it will also return false
217
217
if (waveform.enabled & (1UL << pin)) {
218
- waveform.toDisable = 1UL << pin;
218
+ waveform.toDisable = pin;
219
219
// Must not interfere if Timer is due shortly
220
220
if (T1L > IRQLATENCY + DELTAIRQ) {
221
221
timer1_write (microsecondsToClockCycles (1 ));
222
222
}
223
- while (waveform.toDisable ) {
223
+ while (waveform.toDisable >= 0 ) {
224
224
/* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
225
225
}
226
226
}
@@ -242,21 +242,45 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
242
242
static int nextPin = 0 ;
243
243
244
244
const uint32_t isrStartCcy = ESP.getCycleCount ();
245
-
246
- if (waveform.toEnable || waveform.toDisable ) {
245
+ const uint32_t toSetMask = waveform.toSet >= 0 ? 1UL << waveform.toSet : 0 ;
246
+ const uint32_t toDisableMask = waveform.toDisable >= 0 ? 1UL << waveform.toDisable : 0 ;
247
+ if ((toSetMask && !(waveform.enabled & toSetMask)) || toDisableMask) {
247
248
// Handle enable/disable requests from main app.
248
- waveform.enabled = (waveform.enabled & ~waveform.toDisable ) | waveform.toEnable ; // Set the requested waveforms on/off
249
- waveform.toEnable = 0 ;
250
- waveform.toDisable = 0 ;
249
+ waveform.enabled = (waveform.enabled & ~toDisableMask) | toSetMask; // Set the requested waveforms on/off
251
250
// 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)
252
251
startPin = __builtin_ffs (waveform.enabled ) - 1 ;
253
252
// Find the last bit by subtracting off GCC's count-leading-zeros (no offset in this one)
254
253
endPin = 32 - __builtin_clz (waveform.enabled );
254
+ waveform.toDisable = -1 ;
255
+ }
256
+
257
+ uint32_t now = ESP.getCycleCount ();
258
+
259
+ if (toSetMask) {
260
+ Waveform& wave = waveform.pins [waveform.toSet ];
261
+ switch (wave.mode ) {
262
+ case WaveformMode::INIT:
263
+ waveform.states &= ~toSetMask; // Clear the state of any just started
264
+ wave.nextPeriodCcy = (wave.alignPhase >= 0 && waveform.enabled & (1UL << wave.alignPhase )) ?
265
+ waveform.pins [wave.alignPhase ].nextPeriodCcy + wave.nextPeriodCcy : now;
266
+ wave.nextEventCcy = wave.nextPeriodCcy ;
267
+ if (!wave.expiryCcy ) {
268
+ wave.mode = WaveformMode::INFINITE;
269
+ break ;
270
+ }
271
+ // fall through
272
+ case WaveformMode::UPDATEEXPIRY:
273
+ wave.expiryCcy += wave.nextPeriodCcy ; // in WaveformMode::UPDATEEXPIRY, expiryCcy temporarily holds relative CPU cycle count
274
+ wave.mode = WaveformMode::EXPIRES;
275
+ break ;
276
+ default :
277
+ break ;
278
+ }
279
+ waveform.toSet = -1 ;
255
280
}
256
281
257
282
// Exit the loop if the next event, if any, is sufficiently distant.
258
283
const uint32_t isrTimeoutCcy = isrStartCcy + ISRTIMEOUTCCYS;
259
- uint32_t now = ESP.getCycleCount ();
260
284
uint32_t nextTimerCcy;
261
285
bool busy = waveform.enabled ;
262
286
if (!busy) {
@@ -265,7 +289,6 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
265
289
else if (!(waveform.enabled & (1UL << nextPin))) {
266
290
nextPin = startPin;
267
291
}
268
- bool initPins = true ;
269
292
while (busy) {
270
293
nextTimerCcy = now + MAXIRQCCYS;
271
294
int stopPin = nextPin;
@@ -277,28 +300,6 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
277
300
278
301
Waveform& wave = waveform.pins [pin];
279
302
280
- if (initPins) {
281
- switch (wave.mode ) {
282
- case WaveformMode::INIT:
283
- waveform.states &= ~(1UL << pin); // Clear the state of any just started
284
- wave.nextPeriodCcy = (waveform.enabled & (1UL << wave.alignPhase )) ?
285
- waveform.pins [wave.alignPhase ].nextPeriodCcy + wave.nextPeriodCcy : now;
286
- wave.nextEventCcy = wave.nextPeriodCcy ;
287
- if (!wave.expiryCcy ) {
288
- wave.mode = WaveformMode::INFINITE;
289
- break ;
290
- }
291
- // fall through
292
- case WaveformMode::UPDATEEXPIRY:
293
- wave.expiryCcy += wave.nextPeriodCcy ; // in WaveformMode::UPDATEEXPIRY, expiryCcy temporarily holds relative CPU cycle count
294
- wave.mode = WaveformMode::EXPIRES;
295
- initPins = false ; // only one pin per IRQ
296
- break ;
297
- default :
298
- break ;
299
- }
300
- }
301
-
302
303
int32_t overshootCcys = now - wave.nextEventCcy ;
303
304
if (overshootCcys >= 0 ) {
304
305
if (WaveformMode::EXPIRES == wave.mode && wave.nextEventCcy == wave.expiryCcy ) {
@@ -328,7 +329,7 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
328
329
// preceeding period had zero idle cycle, continue direct into new duty cycle
329
330
if (fwdPeriods) {
330
331
wave.nextPeriodCcy += fwdPeriods * wave.periodCcys ;
331
- // adapt expiry such that it occurs during intended cycle
332
+ // adapt expiry such that it occurs during intended cycle
332
333
if (WaveformMode::EXPIRES == wave.mode )
333
334
wave.expiryCcy += fwdPeriods * wave.periodCcys ;
334
335
}
@@ -391,7 +392,6 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
391
392
392
393
} while ((pin = (pin < endPin) ? pin + 1 : startPin, pin != stopPin));
393
394
394
- initPins = false ;
395
395
now = ESP.getCycleCount ();
396
396
const int32_t timerMarginCcys = isrTimeoutCcy - nextTimerCcy;
397
397
busy = timerMarginCcys > 0 ;
0 commit comments