41
41
#include "ets_sys.h"
42
42
#include "core_esp8266_waveform.h"
43
43
44
- // Need speed, not size, here
45
- #pragma GCC optimize ("O2")
46
-
47
44
// Maximum delay between IRQs
48
45
#define MAXIRQUS (10000)
49
46
50
47
// If the cycles from now to an event are below this value, perform it anyway since IRQs take longer than this
51
48
#define CYCLES_FLUFF (100)
52
49
53
- // Macro to get count of predefined array elements
54
- #define countof (a ) ((size_t)(sizeof(a)/sizeof(a[0])))
55
-
56
- // Set/clear *any* GPIO
57
- #define SetGPIOPin (a ) do { if (a < 16) { GPOS |= (1<<a); } else { GP16O |= 1; } } while (0)
58
- #define ClearGPIOPin (a ) do { if (a < 16) { GPOC |= (1<<a); } else { GP16O &= ~1; } } while (0)
59
- // Set/clear GPIO 0-15
50
+ // Set/clear GPIO 0-15 by bitmask
60
51
#define SetGPIO (a ) do { GPOS = a; } while (0)
61
52
#define ClearGPIO (a ) do { GPOC = a; } while (0)
62
53
54
+ // Convert from us to ESP8266 clocks
55
+ #define MicrosecondsToCycles (m ) (clockCyclesPerMicrosecond() * (m))
56
+
63
57
// Waveform generator can create tones, PWM, and servos
64
58
typedef struct {
65
- uint32_t nextServiceCycle ; // ESP cycle timer when a transition required
66
- uint32_t timeLeftCycles ; // For time-limited waveform, how many ESP cycles left
67
- const uint16_t gpioMask ; // Mask instead of value to speed IRQ loop
68
- const uint16_t gpio16Mask ; // Mask instead of value to speed IRQ loop
69
- unsigned state : 1 ; // Current state of this pin
70
- unsigned nextTimeHighCycles : 31 ; // Copy over low->high to keep smooth waveform
71
- unsigned enabled : 1 ; // Is this GPIO generating a waveform?
72
- unsigned nextTimeLowCycles : 31 ; // Copy over high->low to keep smooth waveform
59
+ uint32_t nextServiceCycle ; // ESP cycle timer when a transition required
60
+ uint32_t timeLeftCycles ; // For time-limited waveform, how many ESP cycles left
61
+ uint32_t nextTimeHighCycles ; // Copy over low->high to keep smooth waveform
62
+ uint32_t nextTimeLowCycles ; // Copy over high->low to keep smooth waveform
73
63
} Waveform ;
74
64
75
65
// These can be accessed in interrupts, so ensure to bracket access with SEI/CLI
76
- static Waveform waveform [] = {
77
- {0 , 0 , 1 <<0 , 0 , 0 , 0 , 0 , 0 }, // GPIO0
78
- {0 , 0 , 1 <<1 , 0 , 0 , 0 , 0 , 0 }, // GPIO1
79
- {0 , 0 , 1 <<2 , 0 , 0 , 0 , 0 , 0 },
80
- {0 , 0 , 1 <<3 , 0 , 0 , 0 , 0 , 0 },
81
- {0 , 0 , 1 <<4 , 0 , 0 , 0 , 0 , 0 },
82
- {0 , 0 , 1 <<5 , 0 , 0 , 0 , 0 , 0 },
83
- // GPIOS 6-8 not allowed, used for flash
84
- // GPIO 9 and 10 only allowed in 2-bit flash mode
85
- #if !isFlashInterfacePin (9 )
86
- {0 , 0 , 1 <<9 , 0 , 0 , 0 , 0 , 0 },
87
- {0 , 0 , 1 <<10 , 0 , 0 , 0 , 0 , 0 },
88
- #endif
89
- // GPIO 11 not allowed, used for flash
90
- {0 , 0 , 1 <<12 , 0 , 0 , 0 , 0 , 0 },
91
- {0 , 0 , 1 <<13 , 0 , 0 , 0 , 0 , 0 },
92
- {0 , 0 , 1 <<14 , 0 , 0 , 0 , 0 , 0 },
93
- {0 , 0 , 1 <<15 , 0 , 0 , 0 , 0 , 0 },
94
- {0 , 0 , 0 , 1 , 0 , 0 , 0 , 0 } // GPIO16
95
- };
66
+ static Waveform waveform [17 ]; // State of all possible pins
67
+ static volatile uint32_t waveformState = 0 ; // Is the pin high or low
68
+ static volatile uint32_t waveformEnabled = 0 ; // Is it actively running
96
69
97
- static uint32_t (* timer1CB )() = NULL ;;
70
+ // Enable lock-free by only allowing updates to waveformState and waveformEnabled from IRQ service routine
71
+ static volatile uint32_t waveformToEnable = 0 ; // Message from startWaveform to IRQ to actuall begin a wvf
72
+ static volatile uint32_t waveformToDisable = 0 ; // Message from startWaveform to IRQ to actuall begin a wvf
98
73
74
+ static uint32_t (* timer1CB )() = NULL ;
99
75
100
- // Helper functions
101
- static inline ICACHE_RAM_ATTR uint32_t MicrosecondsToCycles (uint32_t microseconds ) {
102
- return clockCyclesPerMicrosecond () * microseconds ;
103
- }
104
76
105
- static inline ICACHE_RAM_ATTR uint32_t min_u32 (uint32_t a , uint32_t b ) {
106
- if (a < b ) {
107
- return a ;
108
- }
109
- return b ;
110
- }
111
-
112
- static inline ICACHE_RAM_ATTR void ReloadTimer (uint32_t a ) {
113
- // Below a threshold you actually miss the edge IRQ, so ensure enough time
114
- if (a > 32 ) {
115
- timer1_write (a );
116
- } else {
117
- timer1_write (32 );
118
- }
119
- }
77
+ // Non-speed critical bits
78
+ #pragma GCC optimize ("Os")
120
79
121
80
static inline ICACHE_RAM_ATTR uint32_t GetCycleCount () {
122
81
uint32_t ccount ;
@@ -126,7 +85,7 @@ static inline ICACHE_RAM_ATTR uint32_t GetCycleCount() {
126
85
127
86
// Interrupt on/off control
128
87
static ICACHE_RAM_ATTR void timer1Interrupt ();
129
- static uint8_t timerRunning = false;
88
+ static bool timerRunning = false;
130
89
static uint32_t lastCycleCount = 0 ; // Last ESP cycle counter on running the interrupt routine
131
90
132
91
static void initTimer () {
@@ -150,122 +109,129 @@ void setTimer1Callback(uint32_t (*fn)()) {
150
109
timer1CB = fn ;
151
110
if (!timerRunning && fn ) {
152
111
initTimer ();
153
- } else if (timerRunning && !fn ) {
154
- int cnt = 0 ;
155
- for (size_t i = 0 ; i < countof (waveform ); i ++ ) {
156
- cnt += waveform [i ].enabled ? 1 : 0 ;
157
- }
158
- if (!cnt ) {
159
- deinitTimer ();
160
- }
112
+ timer1_write (MicrosecondsToCycles (1 )); // Cause an interrupt post-haste
113
+ } else if (timerRunning && !fn && !waveformEnabled ) {
114
+ deinitTimer ();
161
115
}
162
- ReloadTimer (MicrosecondsToCycles (1 )); // Cause an interrupt post-haste
163
116
}
164
117
165
118
// Start up a waveform on a pin, or change the current one. Will change to the new
166
119
// waveform smoothly on next low->high transition. For immediate change, stopWaveform()
167
120
// first, then it will immediately begin.
168
121
int startWaveform (uint8_t pin , uint32_t timeHighUS , uint32_t timeLowUS , uint32_t runTimeUS ) {
169
- Waveform * wave = NULL ;
170
- for (size_t i = 0 ; i < countof (waveform ); i ++ ) {
171
- if (((pin == 16 ) && waveform [i ].gpio16Mask == 1 ) || ((pin != 16 ) && (waveform [i ].gpioMask == 1 <<pin ))) {
172
- wave = (Waveform * ) & (waveform [i ]);
173
- break ;
174
- }
175
- }
176
- if (!wave ) {
122
+ if ((pin > 16 ) || isFlashInterfacePin (pin )) {
177
123
return false;
178
124
}
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
-
185
- wave -> nextTimeHighCycles = MicrosecondsToCycles (timeHighUS ) - 70 ; // Take out some time for IRQ codepath
186
- wave -> nextTimeLowCycles = MicrosecondsToCycles ( timeLowUS ) - 70 ; // Take out some time for IRQ codepath
125
+ Waveform * wave = & waveform [ pin ];
126
+ #if F_CPU == 160000000
127
+ wave -> nextTimeHighCycles = MicrosecondsToCycles ( timeHighUS ) - 140 ; // Take out some time for IRQ codepath
128
+ wave -> nextTimeLowCycles = MicrosecondsToCycles ( timeLowUS ) - 140 ; // Take out some time for IRQ codepath
129
+ #else
130
+ wave -> nextTimeHighCycles = MicrosecondsToCycles ( timeHighUS ) > 280 ? MicrosecondsToCycles ( timeHighUS ) - 280 : MicrosecondsToCycles ( timeHighUS ); // Take out some time for IRQ codepath
131
+ wave -> nextTimeLowCycles = MicrosecondsToCycles (timeLowUS ) > 280 ? MicrosecondsToCycles ( timeLowUS ) - 280 : MicrosecondsToCycles ( timeLowUS ) ; // Take out some time for IRQ codepath
132
+ #endif
187
133
wave -> timeLeftCycles = MicrosecondsToCycles (runTimeUS );
188
- if (! wave -> enabled ) {
189
- wave -> state = 0 ;
134
+ uint32_t mask = 1 << pin ;
135
+ if (! waveformEnabled && mask ) {
190
136
// Actually set the pin high or low in the IRQ service to guarantee times
191
137
wave -> nextServiceCycle = GetCycleCount () + MicrosecondsToCycles (1 );
192
- wave -> enabled = 1 ;
138
+ waveformToEnable |= mask ;
193
139
if (!timerRunning ) {
194
140
initTimer ();
195
141
}
196
- ReloadTimer (MicrosecondsToCycles (1 )); // Cause an interrupt post-haste
142
+ timer1_write (MicrosecondsToCycles (1 )); // Cause an interrupt post-haste
143
+ while (waveformToEnable ) {
144
+ delay (1 ); // Wait for waveform to update
145
+ }
197
146
}
198
147
199
- // Re-enable interrupts here since we're done with the update
200
- ets_intr_unlock ();
201
-
202
148
return true;
203
149
}
204
150
151
+ // Speed critical bits
152
+ #pragma GCC optimize ("O2")
153
+ // Normally would not want two copies like this, but due to different
154
+ // optimization levels the inline attribute gets lost if we try the
155
+ // other version.
156
+
157
+ static inline ICACHE_RAM_ATTR uint32_t GetCycleCountIRQ () {
158
+ uint32_t ccount ;
159
+ __asm__ __volatile__("esync; rsr %0,ccount" :"=a" (ccount ));
160
+ return ccount ;
161
+ }
162
+
163
+ static inline ICACHE_RAM_ATTR uint32_t min_u32 (uint32_t a , uint32_t b ) {
164
+ if (a < b ) {
165
+ return a ;
166
+ }
167
+ return b ;
168
+ }
169
+
205
170
// Stops a waveform on a pin
206
171
int ICACHE_RAM_ATTR stopWaveform (uint8_t pin ) {
207
172
// Can't possibly need to stop anything if there is no timer active
208
173
if (!timerRunning ) {
209
174
return false;
210
175
}
211
-
212
- for (size_t i = 0 ; i < countof (waveform ); i ++ ) {
213
- if (!waveform [i ].enabled ) {
214
- continue ; // Skip fast to next one, can't need to stop this one since it's not running
215
- }
216
- if (((pin == 16 ) && waveform [i ].gpio16Mask ) || ((pin != 16 ) && (waveform [i ].gpioMask == 1 <<pin ))) {
217
- // Note that there is no interrupt unsafety here. The IRQ can only ever change .enabled from 1->0
218
- // We're also doing that, so even if an IRQ occurred it would still stay as 0.
219
- waveform [i ].enabled = 0 ;
220
- int cnt = timer1CB ? 1 : 0 ;
221
- for (size_t i = 0 ; (cnt == 0 ) && (i < countof (waveform )); i ++ ) {
222
- cnt += waveform [i ].enabled ? 1 : 0 ;
223
- }
224
- if (!cnt ) {
225
- deinitTimer ();
226
- }
227
- return true;
228
- }
176
+ // If user sends in a pin >16 but <32, this will always point to a 0 bit
177
+ // If they send >=32, then the shift will result in 0 and it will also return false
178
+ uint32_t mask = 1 <<pin ;
179
+ if (!(waveformEnabled & mask )) {
180
+ return false; //It's not running, nothing to do here
181
+ }
182
+ waveformToDisable |= mask ;
183
+ timer1_write (MicrosecondsToCycles (1 ));
184
+ while (waveformToDisable ) {
185
+ delay (1 ); // Wait for IRQ to update
229
186
}
230
- return false;
187
+ if (!waveformEnabled && !timer1CB ) {
188
+ deinitTimer ();
189
+ }
190
+ return true;
231
191
}
232
192
233
193
static ICACHE_RAM_ATTR void timer1Interrupt () {
234
194
uint32_t nextEventCycles ;
235
- #if F_CPU == 160000000
195
+ #if F_CPU == 160000000
236
196
uint8_t cnt = 20 ;
237
- #else
238
- uint8_t cnt = 10 ;
239
- #endif
197
+ #else
198
+ uint8_t cnt = 5 ;
199
+ #endif
200
+
201
+ // Handle enable/disable requests from main app.
202
+ waveformEnabled = (waveformEnabled & ~waveformToDisable ) | waveformToEnable ; // Set the requested waveforms on/off
203
+ waveformState &= ~waveformToEnable ; // And clear the state of any just started
204
+ waveformToEnable = 0 ;
205
+ waveformToDisable = 0 ;
240
206
241
207
do {
242
208
nextEventCycles = MicrosecondsToCycles (MAXIRQUS );
243
- for (size_t i = 0 ; i < countof (waveform ); i ++ ) {
244
- Waveform * wave = & waveform [i ];
245
- uint32_t now ;
209
+ for (size_t i = 0 ; i <= 16 ; i ++ ) {
210
+ uint32_t mask = 1 <<i ;
246
211
247
212
// If it's not on, ignore!
248
- if (!wave -> enabled ) {
213
+ if (!( waveformEnabled & mask ) ) {
249
214
continue ;
250
215
}
251
216
217
+ Waveform * wave = & waveform [i ];
218
+ uint32_t now ;
219
+
252
220
// Check for toggles
253
- now = GetCycleCount ();
221
+ now = GetCycleCountIRQ ();
254
222
int32_t cyclesToGo = wave -> nextServiceCycle - now ;
255
223
if (cyclesToGo < 0 ) {
256
- wave -> state = !wave -> state ;
257
- if (wave -> state ) {
258
- SetGPIO (wave -> gpioMask );
259
- if (wave -> gpio16Mask ) {
260
- GP16O |= wave -> gpio16Mask ; // GPIO16 write slow as it's RMW
261
- }
224
+ waveformState ^= mask ;
225
+ if (waveformState & mask ) {
226
+ if (i == 16 ) GP16O |= 1 ; // GPIO16 write slow as it's RMW
227
+ else SetGPIO (mask );
228
+
262
229
wave -> nextServiceCycle = now + wave -> nextTimeHighCycles ;
263
230
nextEventCycles = min_u32 (nextEventCycles , wave -> nextTimeHighCycles );
264
231
} else {
265
- ClearGPIO (wave -> gpioMask );
266
- if (wave -> gpio16Mask ) {
267
- GP16O &= ~wave -> gpio16Mask ;
268
- }
232
+ if (i == 16 ) GP16O &= ~1 ; // GPIO16 write slow as it's RMW
233
+ else ClearGPIO (mask );
234
+
269
235
wave -> nextServiceCycle = now + wave -> nextTimeLowCycles ;
270
236
nextEventCycles = min_u32 (nextEventCycles , wave -> nextTimeLowCycles );
271
237
}
@@ -276,20 +242,21 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
276
242
}
277
243
} while (-- cnt && (nextEventCycles < MicrosecondsToCycles (4 )));
278
244
279
- uint32_t curCycleCount = GetCycleCount ();
245
+ uint32_t curCycleCount = GetCycleCountIRQ ();
280
246
uint32_t deltaCycles = curCycleCount - lastCycleCount ;
281
247
lastCycleCount = curCycleCount ;
282
248
283
249
// Check for timed-out waveforms out of the high-frequency toggle loop
284
- for (size_t i = 0 ; i < countof ( waveform ) ; i ++ ) {
250
+ for (size_t i = 0 ; i <= 16 ; i ++ ) {
285
251
Waveform * wave = & waveform [i ];
286
- if (wave -> enabled && wave -> timeLeftCycles ) {
252
+ uint32_t mask = 1 <<i ;
253
+ if ((waveformEnabled & mask ) && wave -> timeLeftCycles ) {
287
254
// Check for unsigned underflow with new > old
288
255
if (deltaCycles >= wave -> timeLeftCycles ) {
289
256
// Done, remove!
290
- wave -> enabled = false ;
291
- ClearGPIO ( wave -> gpioMask ) ;
292
- GP16O &= ~ wave -> gpio16Mask ;
257
+ waveformEnabled &= ~ mask ;
258
+ if ( i == 16 ) GP16O &= ~ 1 ;
259
+ else ClearGPIO ( mask ) ;
293
260
} else {
294
261
uint32_t newTimeLeftCycles = wave -> timeLeftCycles - deltaCycles ;
295
262
wave -> timeLeftCycles = newTimeLeftCycles ;
@@ -301,20 +268,22 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
301
268
nextEventCycles = min_u32 (nextEventCycles , timer1CB ());
302
269
}
303
270
304
- #if F_CPU == 160000000
305
- if (nextEventCycles <= 5 * MicrosecondsToCycles (1 )) {
306
- nextEventCycles = MicrosecondsToCycles (1 ) / 2 ;
271
+ #if F_CPU == 160000000
272
+ if (nextEventCycles < MicrosecondsToCycles (5 )) {
273
+ nextEventCycles = MicrosecondsToCycles (1 );
307
274
} else {
308
- nextEventCycles -= 5 * MicrosecondsToCycles (1 );
275
+ nextEventCycles -= MicrosecondsToCycles ( 4 ) + MicrosecondsToCycles (1 )/ 2 ;
309
276
}
310
277
nextEventCycles = nextEventCycles >> 1 ;
311
- #else
312
- if (nextEventCycles <= 6 * MicrosecondsToCycles (1 )) {
313
- nextEventCycles = MicrosecondsToCycles (1 ) / 2 ;
278
+ #else
279
+ if (nextEventCycles < MicrosecondsToCycles (8 )) {
280
+ nextEventCycles = MicrosecondsToCycles (2 ) ;
314
281
} else {
315
- nextEventCycles -= 6 * MicrosecondsToCycles (1 );
282
+ nextEventCycles -= MicrosecondsToCycles (6 );
316
283
}
317
- #endif
284
+ #endif
318
285
319
- ReloadTimer (nextEventCycles );
286
+ // Do it here instead of global function to save time and because we know it's edge-IRQ
287
+ T1L = nextEventCycles ; // Already know we're in range by MAXIRQUS
288
+ TEIE |= TEIE1 ; //edge int enable
320
289
}
0 commit comments