48
48
#define SetGPIO (a ) do { GPOS = a; } while (0)
49
49
#define ClearGPIO (a ) do { GPOC = a; } while (0)
50
50
51
- // Convert from us to ESP8266 clocks
52
- #define MicrosecondsToCycles (m ) (clockCyclesPerMicrosecond() * (m))
53
-
54
51
// Waveform generator can create tones, PWM, and servos
55
52
typedef struct {
56
53
uint32_t nextServiceCycle ; // ESP cycle timer when a transition required
@@ -59,14 +56,13 @@ typedef struct {
59
56
uint32_t nextTimeLowCycles ; // Copy over high->low to keep smooth waveform
60
57
} Waveform ;
61
58
62
- // These can be accessed in interrupts, so ensure to bracket access with SEI/CLI
63
59
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
66
62
67
63
// 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
70
66
71
67
static uint32_t (* timer1CB )() = NULL ;
72
68
@@ -104,7 +100,7 @@ void setTimer1Callback(uint32_t (*fn)()) {
104
100
timer1CB = fn ;
105
101
if (!timerRunning && fn ) {
106
102
initTimer ();
107
- timer1_write (MicrosecondsToCycles (1 )); // Cause an interrupt post-haste
103
+ timer1_write (microsecondsToClockCycles (1 )); // Cause an interrupt post-haste
108
104
} else if (timerRunning && !fn && !waveformEnabled ) {
109
105
deinitTimer ();
110
106
}
@@ -119,22 +115,27 @@ int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t
119
115
}
120
116
Waveform * wave = & waveform [pin ];
121
117
// 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 ;
125
121
if (runTimeUS && !wave -> expiryCycle ) {
126
122
wave -> expiryCycle = 1 ; // expiryCycle==0 means no timeout, so avoid setting it
127
123
}
128
124
129
125
uint32_t mask = 1 <<pin ;
130
126
if (!(waveformEnabled & mask )) {
131
127
// 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 );
133
129
waveformToEnable |= mask ;
134
130
if (!timerRunning ) {
135
131
initTimer ();
132
+ timer1_write (microsecondsToClockCycles (10 ));
133
+ } else {
134
+ // Ensure timely service....
135
+ if (T1L > microsecondsToClockCycles (10 )) {
136
+ timer1_write (microsecondsToClockCycles (10 ));
137
+ }
136
138
}
137
- timer1_write (MicrosecondsToCycles (10 )); // Cause an interrupt post-haste
138
139
while (waveformToEnable ) {
139
140
delay (0 ); // Wait for waveform to update
140
141
}
@@ -172,106 +173,125 @@ int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
172
173
// If they send >=32, then the shift will result in 0 and it will also return false
173
174
uint32_t mask = 1 <<pin ;
174
175
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
176
177
}
177
178
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 ));
181
182
}
182
183
while (waveformToDisable ) {
183
- delay (1 ); // Wait for IRQ to update
184
+ /* no-op */ // Can't delay() since stopWaveform may be called from an IRQ
184
185
}
185
186
if (!waveformEnabled && !timer1CB ) {
186
187
deinitTimer ();
187
188
}
188
189
return true;
189
190
}
190
191
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.
191
195
#if F_CPU == 80000000
192
- #define DELTAIRQ (MicrosecondsToCycles (3))
196
+ #define DELTAIRQ (microsecondsToClockCycles (3))
193
197
#else
194
- #define DELTAIRQ (MicrosecondsToCycles (2))
198
+ #define DELTAIRQ (microsecondsToClockCycles (2))
195
199
#endif
196
200
197
- static int startPin = 0 ;
198
- static int endPin = 0 ;
199
201
200
202
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 );
203
211
204
212
if (waveformToEnable || waveformToDisable ) {
205
213
// Handle enable/disable requests from main app.
206
214
waveformEnabled = (waveformEnabled & ~waveformToDisable ) | waveformToEnable ; // Set the requested waveforms on/off
207
215
waveformState &= ~waveformToEnable ; // And clear the state of any just started
208
216
waveformToEnable = 0 ;
209
217
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)
210
219
startPin = __builtin_ffs (waveformEnabled ) - 1 ;
220
+ // Find the last bit by subtracting off GCC's count-leading-zeros (no offset in this one)
211
221
endPin = 32 - __builtin_clz (waveformEnabled );
212
222
}
213
223
214
224
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
+ }
224
235
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
+ }
227
253
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 );
237
274
}
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 );
250
275
} 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 );
256
278
}
257
- } else {
258
- uint32_t deltaCycles = wave -> nextServiceCycle - now ;
259
- nextEventCycles = min_u32 (nextEventCycles , deltaCycles );
260
279
}
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)
269
288
270
289
if (timer1CB ) {
271
290
nextEventCycles = min_u32 (nextEventCycles , timer1CB ());
272
291
}
273
- if (nextEventCycles < MicrosecondsToCycles (10 )) {
274
- nextEventCycles = MicrosecondsToCycles (10 );
292
+
293
+ if (nextEventCycles < microsecondsToClockCycles (10 )) {
294
+ nextEventCycles = microsecondsToClockCycles (10 );
275
295
}
276
296
nextEventCycles -= DELTAIRQ ;
277
297
@@ -281,5 +301,5 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
281
301
#else
282
302
T1L = nextEventCycles ; // Already know we're in range by MAXIRQUS
283
303
#endif
284
- TEIE |= TEIE1 ; //edge int enable
304
+ TEIE |= TEIE1 ; // Edge int enable
285
305
}
0 commit comments