44
44
#include " ets_sys.h"
45
45
#include < atomic>
46
46
47
- extern " C" {
48
-
49
- // Maximum delay between IRQs, 1Hz
50
- constexpr int32_t MAXIRQCCYS = microsecondsToClockCycles(10000 );
47
+ // Maximum delay between IRQs, Timer1, <= 2^23 / 80MHz
48
+ constexpr int32_t MAXIRQTICKSCCYS = microsecondsToClockCycles(100000 );
51
49
// Maximum servicing time for any single IRQ
52
50
constexpr uint32_t ISRTIMEOUTCCYS = microsecondsToClockCycles(14 );
53
51
// The SDK and hardware take some time to actually get to our NMI code, so
54
52
// decrement the next IRQ's timer value by a bit so we can actually catch the
55
53
// real CPU cycle count we want for the waveforms.
56
- constexpr int32_t DELTAIRQ = clockCyclesPerMicrosecond() == 160 ?
57
- microsecondsToClockCycles (1 ) >> 1 : microsecondsToClockCycles(1 );
54
+ constexpr int32_t DELTAIRQCCYS = clockCyclesPerMicrosecond() == 160 ?
55
+ microsecondsToClockCycles (3 ) >> 1 : microsecondsToClockCycles(3 );
58
56
// The latency between in-ISR rearming of the timer and the earliest firing
59
- constexpr int32_t IRQLATENCY = clockCyclesPerMicrosecond() == 160 ?
60
- (microsecondsToClockCycles(5 ) / 2 ) >> 1 : ( microsecondsToClockCycles(5 ) / 2 ) ;
57
+ constexpr int32_t IRQLATENCYCCYS = clockCyclesPerMicrosecond() == 160 ?
58
+ (microsecondsToClockCycles(3 ) / 2 ) >> 1 : microsecondsToClockCycles(3 ) / 2 ;
61
59
62
60
// Set/clear GPIO 0-15 by bitmask
63
61
#define SetGPIO (a ) do { GPOS = a; } while (0 )
@@ -120,7 +118,7 @@ static void initTimer() {
120
118
ETS_FRC_TIMER1_NMI_INTR_ATTACH (timer1Interrupt);
121
119
timer1_enable (TIM_DIV1, TIM_EDGE, TIM_SINGLE);
122
120
waveform.timer1Running = true ;
123
- timer1_write (microsecondsToClockCycles (1 )); // Cause an interrupt post-haste
121
+ timer1_write (CPU2X & 1 ? microsecondsToClockCycles ( 1 ) >> 1 : microsecondsToClockCycles (1 )); // Cause an interrupt post-haste
124
122
}
125
123
126
124
static void ICACHE_RAM_ATTR deinitTimer () {
@@ -130,6 +128,8 @@ static void ICACHE_RAM_ATTR deinitTimer() {
130
128
waveform.timer1Running = false ;
131
129
}
132
130
131
+ extern " C" {
132
+
133
133
// Set a callback. Pass in NULL to stop it
134
134
void setTimer1Callback (uint32_t (*fn)()) {
135
135
waveform.timer1CB = fn;
@@ -154,12 +154,12 @@ int startWaveform(uint8_t pin, uint32_t highUS, uint32_t lowUS,
154
154
int startWaveformClockCycles (uint8_t pin, uint32_t highCcys, uint32_t lowCcys,
155
155
uint32_t runTimeCcys, int8_t alignPhase, uint32_t phaseOffsetCcys, bool autoPwm) {
156
156
uint32_t periodCcys = highCcys + lowCcys;
157
- if (periodCcys < MAXIRQCCYS ) {
157
+ if (periodCcys < MAXIRQTICKSCCYS ) {
158
158
if (!highCcys) {
159
- periodCcys = (MAXIRQCCYS / periodCcys) * periodCcys;
159
+ periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys;
160
160
}
161
161
else if (!lowCcys) {
162
- highCcys = periodCcys = (MAXIRQCCYS / periodCcys) * periodCcys;
162
+ highCcys = periodCcys = (MAXIRQTICKSCCYS / periodCcys) * periodCcys;
163
163
}
164
164
}
165
165
// sanity checks, including mixed signed/unsigned arithmetic safety
@@ -193,9 +193,9 @@ int startWaveformClockCycles(uint8_t pin, uint32_t highCcys, uint32_t lowCcys,
193
193
if (!waveform.timer1Running ) {
194
194
initTimer ();
195
195
}
196
- else if (T1L > IRQLATENCY + DELTAIRQ ) {
197
- // Must not interfere if Timer is due shortly, cluster phases to reduce interrupt load
198
- timer1_write (microsecondsToClockCycles (1 ));
196
+ else if (((CPU2X & 1 ) ? T1V << 1 : T1V) > IRQLATENCYCCYS + DELTAIRQCCYS ) {
197
+ // Must not interfere if Timer is due shortly
198
+ timer1_write (CPU2X & 1 ? microsecondsToClockCycles ( 1 ) >> 1 : microsecondsToClockCycles (1 ));
199
199
}
200
200
}
201
201
else {
@@ -228,8 +228,8 @@ int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
228
228
if (waveform.enabled & (1UL << pin)) {
229
229
waveform.toDisable = pin;
230
230
// Must not interfere if Timer is due shortly
231
- if (T1L > IRQLATENCY + DELTAIRQ ) {
232
- timer1_write (microsecondsToClockCycles (1 ));
231
+ if (((CPU2X & 1 ) ? T1V << 1 : T1V) > IRQLATENCYCCYS + DELTAIRQCCYS ) {
232
+ timer1_write (CPU2X & 1 ? microsecondsToClockCycles ( 1 ) >> 1 : microsecondsToClockCycles (1 ));
233
233
}
234
234
std::atomic_thread_fence (std::memory_order_acq_rel);
235
235
while (waveform.toDisable >= 0 ) {
@@ -243,6 +243,8 @@ int ICACHE_RAM_ATTR stopWaveform(uint8_t pin) {
243
243
return true ;
244
244
}
245
245
246
+ };
247
+
246
248
// Speed critical bits
247
249
#pragma GCC optimize ("O2")
248
250
@@ -296,7 +298,7 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
296
298
bool busy;
297
299
if (!waveform.enabled ) {
298
300
busy = false ;
299
- waveform.nextEventCcy = ESP.getCycleCount () + MAXIRQCCYS ;
301
+ waveform.nextEventCcy = ESP.getCycleCount () + MAXIRQTICKSCCYS ;
300
302
}
301
303
else {
302
304
busy = static_cast <int32_t >(isrTimeoutCcy - waveform.nextEventCcy ) > 0 ;
@@ -311,7 +313,7 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
311
313
do {
312
314
now = ESP.getCycleCount ();
313
315
} while (static_cast <int32_t >(waveform.nextEventCcy - now) > 0 );
314
- waveform.nextEventCcy = now + MAXIRQCCYS ;
316
+ waveform.nextEventCcy = now + MAXIRQTICKSCCYS ;
315
317
do {
316
318
// If it's not on, ignore
317
319
if (!(waveform.enabled & (1UL << pin)))
@@ -354,7 +356,7 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
354
356
nextEdgeCcy = wave.endDutyCcy ;
355
357
}
356
358
else if (wave.autoPwm && (overshootCcys << 6 ) > wave.periodCcys && wave.nextEventCcy == wave.endDutyCcy ) {
357
- uint32_t adjPeriods = (( overshootCcys << 6 ) - 1 ) / wave.periodCcys ;
359
+ uint32_t adjPeriods = (overshootCcys << 6 ) / wave.periodCcys ;
358
360
wave.nextPeriodCcy += adjPeriods * wave.periodCcys ;
359
361
// adapt expiry such that it occurs during intended cycle
360
362
if (WaveformMode::EXPIRES == wave.mode ) {
@@ -431,24 +433,20 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
431
433
}
432
434
433
435
// Firing timer too soon, the NMI occurs before ISR has returned.
434
- if (nextTimerCcys <= IRQLATENCY + DELTAIRQ ) {
435
- nextTimerCcys = IRQLATENCY ;
436
+ if (nextTimerCcys <= IRQLATENCYCCYS + DELTAIRQCCYS ) {
437
+ nextTimerCcys = IRQLATENCYCCYS ;
436
438
}
437
- else if (nextTimerCcys >= MAXIRQCCYS ) {
438
- nextTimerCcys = MAXIRQCCYS - DELTAIRQ ;
439
+ else if (nextTimerCcys >= MAXIRQTICKSCCYS + DELTAIRQCCYS ) {
440
+ nextTimerCcys = MAXIRQTICKSCCYS ;
439
441
}
440
442
else {
441
- nextTimerCcys -= DELTAIRQ ;
443
+ nextTimerCcys -= DELTAIRQCCYS ;
442
444
}
443
445
444
- // Do it here instead of global function to save time and because we know it's edge-IRQ
445
- if (CPU2X & 1 ) {
446
- T1L = nextTimerCcys >> 1 ;
447
- }
448
- else {
449
- T1L = nextTimerCcys;
450
- }
446
+ // Register access is fast and edge IRQ was configured before.
447
+ // Timer is 80MHz fixed. 160MHz binaries need scaling,
448
+ // 80MHz binaries in 160MHz boost (SDK) need NMI scaling
449
+ // to maintain duty/idle ratio.
450
+ T1L = CPU2X & 1 ? nextTimerCcys >> 1 : nextTimerCcys;
451
451
TEIE |= TEIE1; // Edge int enable
452
452
}
453
-
454
- };
0 commit comments