Skip to content

Commit 2b1c0f0

Browse files
Support busy-looping for small phase resolution
The closest time between interrupts is about 3us, so rework the waveform generator to support phases down to 1us, while limiting the time in the IRQ handler to avoid WDT or WiFi errors. Waveforms now are based off of absolute cycle, not delta-cycles, and the GPIOs are converted from a bit to a mask (to save clocks in the IRQ) costing about 40 more bytes in global storage and reducing the minimum period down from 3.5us to 1.20us at 160MHz. Steppers still based off of delta-times.
1 parent 6bf48a8 commit 2b1c0f0

File tree

2 files changed

+117
-97
lines changed

2 files changed

+117
-97
lines changed

cores/esp8266/core_esp8266_waveform.c

+114-94
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@
5555
#include <Arduino.h>
5656
#include "core_esp8266_waveform.h"
5757

58+
// Need speed, not size, here
59+
#pragma GCC optimize ("O3")
60+
5861
// Map the IRQ stuff to standard terminology
5962
#define cli() ets_intr_lock()
6063
#define sei() ets_intr_unlock()
@@ -63,7 +66,7 @@
6366
#define MAXIRQUS (10000)
6467

6568
// If the cycles from now to an event are below this value, perform it anyway since IRQs take longer than this
66-
#define CYCLES_FLUFF (200)
69+
#define CYCLES_FLUFF (100)
6770

6871
// Macro to get count of predefined array elements
6972
#define countof(a) ((size_t)(sizeof(a)/sizeof(a[0])))
@@ -77,34 +80,36 @@
7780

7881
// Waveform generator can create tones, PWM, and servos
7982
typedef struct {
80-
uint32_t nextEventCycles;
83+
uint32_t nextServiceCycle;
8184
uint32_t timeHighCycles;
8285
uint32_t timeLowCycles;
8386
uint32_t timeLeftCycles;
8487
// To ensure stable change, only copy these over on low->high transition
85-
unsigned gpioPin : 4; // Check gpioPin16 first
86-
unsigned nextTimeHighCycles : 28;
87-
unsigned gpioPin16 : 1; // Special case for weird IO16
88+
uint16_t gpioMask;
89+
uint16_t gpio16Mask;
90+
// unsigned gpioPin : 4; // Check gpioPin16 first
8891
unsigned state : 1;
92+
unsigned nextTimeHighCycles : 31;
93+
// unsigned gpioPin16 : 1; // Special case for weird IO16
8994
unsigned enabled : 1;
90-
unsigned nextTimeLowCycles : 28;
95+
unsigned nextTimeLowCycles : 31;
9196
} Waveform;
9297

9398
// These can be accessed in interrupts, so ensure to bracket access with SEI/CLI
9499
static Waveform waveform[] = {
95-
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // GPIO0
96-
{0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, // GPIO1
97-
{0, 0, 0, 0, 2, 0, 0, 0, 0, 0},
98-
{0, 0, 0, 0, 3, 0, 0, 0, 0, 0},
99-
{0, 0, 0, 0, 4, 0, 0, 0, 0, 0},
100-
{0, 0, 0, 0, 5, 0, 0, 0, 0, 0},
100+
{0, 0, 0, 0, 1<<0, 0, 0, 0, 0, 0}, // GPIO0
101+
{0, 0, 0, 0, 1<<1, 0, 0, 0, 0, 0}, // GPIO1
102+
{0, 0, 0, 0, 1<<2, 0, 0, 0, 0, 0},
103+
{0, 0, 0, 0, 1<<3, 0, 0, 0, 0, 0},
104+
{0, 0, 0, 0, 1<<4, 0, 0, 0, 0, 0},
105+
{0, 0, 0, 0, 1<<5, 0, 0, 0, 0, 0},
101106
// GPIOS 6-11 not allowed, used for flash
102-
{0, 0, 0, 0, 12, 0, 0, 0, 0, 0},
103-
{0, 0, 0, 0, 13, 0, 0, 0, 0, 0},
104-
{0, 0, 0, 0, 14, 0, 0, 0, 0, 0},
105-
{0, 0, 0, 0, 15, 0, 0, 0, 0, 0},
106-
{0, 0, 0, 0, 0, 0, 1, 0, 0, 0}
107-
}; // GPIO16
107+
{0, 0, 0, 0, 1<<12, 0, 0, 0, 0, 0},
108+
{0, 0, 0, 0, 1<<13, 0, 0, 0, 0, 0},
109+
{0, 0, 0, 0, 1<<14, 0, 0, 0, 0, 0},
110+
{0, 0, 0, 0, 1<<15, 0, 0, 0, 0, 0},
111+
{0, 0, 0, 0, 0, 1, 0, 0, 0, 0} // GPIO16
112+
};
108113

109114

110115
// Maximum umber of moves per stepper queue
@@ -164,8 +169,20 @@ static inline ICACHE_RAM_ATTR uint32_t min_u32(uint32_t a, uint32_t b) {
164169
return b;
165170
}
166171

172+
static inline ICACHE_RAM_ATTR uint32_t min_s32(int32_t a, int32_t b) {
173+
if (a < b) {
174+
return a;
175+
}
176+
return b;
177+
}
178+
167179
static inline ICACHE_RAM_ATTR void ReloadTimer(uint32_t a) {
168-
timer1_write(a);
180+
// Below a threshold you actually miss the edge IRQ, so ensure enough time
181+
if (a > 32) {
182+
timer1_write(a);
183+
} else {
184+
timer1_write(32);
185+
}
169186
}
170187

171188
static inline ICACHE_RAM_ATTR uint32_t GetCycleCount() {
@@ -220,7 +237,7 @@ static inline ICACHE_RAM_ATTR void PopStepper(int i) {
220237
}
221238

222239
// Called by the user to detach a stepper and free memory
223-
int removeStepper(int pin) {
240+
int removeStepper(uint8_t pin) {
224241
sei();
225242
for (int i = 0; i < stepQCnt; i++) {
226243
if (stepQ[i].gpioPin == pin) {
@@ -231,6 +248,7 @@ int removeStepper(int pin) {
231248
return true;
232249
}
233250
}
251+
cli();
234252
return false;
235253
}
236254

@@ -295,7 +313,11 @@ static int PushStepper(int gpioPin, const Motion *nextMove) {
295313
}
296314

297315
// Called by user to add a PWL move to the queue, returns false if there is no space left
298-
int pushStepperMove(int pin, int dir, int sync, uint16_t pulses, float j, float a0, float v0) {
316+
int pushStepperMove(uint8_t pin, int dir, int sync, uint16_t pulses, float j, float a0, float v0) {
317+
if (pin > 15) {
318+
// Only GPIO 0...15 allowed
319+
return false;
320+
}
299321
Motion m;
300322
m.pulses = pulses;
301323
m.j_2 = j / 2.0;
@@ -307,7 +329,7 @@ int pushStepperMove(int pin, int dir, int sync, uint16_t pulses, float j, float
307329
}
308330

309331
// Assign a pin to stepper DIR
310-
int setStepperDirPin(int pin) {
332+
int setStepperDirPin(uint8_t pin) {
311333
if (pin > 16) {
312334
return false;
313335
}
@@ -321,7 +343,7 @@ int setStepperDirPin(int pin) {
321343
int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS) {
322344
Waveform *wave = NULL;
323345
for (size_t i = 0; i < countof(waveform); i++) {
324-
if (((pin == 16) && waveform[i].gpioPin16) || ((pin != 16) && (waveform[i].gpioPin == pin))) {
346+
if (((pin == 16) && waveform[i].gpio16Mask==1) || ((pin != 16) && (waveform[i].gpioMask == 1<<pin))) {
325347
wave = (Waveform*) & (waveform[i]);
326348
break;
327349
}
@@ -334,16 +356,16 @@ int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t
334356
wave->nextTimeLowCycles = MicrosecondsToCycles(timeLowUS);
335357
wave->timeLeftCycles = MicrosecondsToCycles(runTimeUS);
336358
if (!wave->enabled) {
337-
wave->state = 1;
338-
digitalWrite(pin, 1);
339-
wave->timeHighCycles = MicrosecondsToCycles(timeHighUS);
340-
wave->timeLowCycles = MicrosecondsToCycles(timeLowUS);
341-
wave->nextEventCycles = wave->timeHighCycles;
359+
wave->state = 0;
360+
// Actually set the pin high or low in the IRQ service to guarantee times
361+
wave->timeHighCycles = MicrosecondsToCycles(timeHighUS) - 30; // Sub off some of the codepath time
362+
wave->timeLowCycles = MicrosecondsToCycles(timeLowUS) - 30; // Sub off some of the codepath time
363+
wave->nextServiceCycle = GetCycleCount() + MicrosecondsToCycles(1);
342364
wave->enabled = 1;
343365
if (!timerRunning) {
344366
initTimer();
345367
}
346-
ReloadTimer(10); // Cause an interrupt post-haste
368+
ReloadTimer(MicrosecondsToCycles(1)); // Cause an interrupt post-haste
347369
}
348370
cli();
349371
return true;
@@ -352,7 +374,7 @@ int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t
352374
// Stops a waveform on a pin
353375
int stopWaveform(uint8_t pin) {
354376
for (size_t i = 0; i < countof(waveform); i++) {
355-
if (((pin == 16) && waveform[i].gpioPin16) || ((pin != 16) && (waveform[i].gpioPin == pin))) {
377+
if (((pin == 16) && waveform[i].gpio16Mask) || ((pin != 16) && (waveform[i].gpioMask == 1<<pin))) {
356378
sei();
357379
waveform[i].enabled = 0;
358380
int cnt = stepQCnt;
@@ -366,6 +388,7 @@ int stopWaveform(uint8_t pin) {
366388
return true;
367389
}
368390
}
391+
cli();
369392
return false;
370393
}
371394

@@ -463,95 +486,92 @@ static ICACHE_RAM_ATTR uint32_t ProcessSteppers(uint32_t deltaCycles) {
463486
}
464487

465488
static ICACHE_RAM_ATTR void timer1Interrupt() {
489+
uint32_t nextEventCycles;
490+
#if F_CPU == 160000000
491+
uint8_t cnt = 20;
492+
#else
493+
uint8_t cnt = 10;
494+
#endif
495+
496+
do {
497+
nextEventCycles = MicrosecondsToCycles(MAXIRQUS);
498+
for (size_t i = 0; i < countof(waveform); i++) {
499+
Waveform *wave = &waveform[i];
500+
uint32_t now;
501+
502+
// If it's not on, ignore!
503+
if (!wave->enabled) {
504+
continue;
505+
}
506+
507+
// Check for toggles
508+
now = GetCycleCount();
509+
if (now >= wave->nextServiceCycle) {
510+
wave->state = !wave->state;
511+
if (wave->state) {
512+
SetGPIO(wave->gpioMask);
513+
if (wave->gpio16Mask) {
514+
GP16O |= wave->gpio16Mask; // GPIO16 write slow as it's RMW
515+
}
516+
wave->nextServiceCycle = now + wave->timeHighCycles;
517+
wave->timeHighCycles = wave->nextTimeHighCycles;
518+
nextEventCycles = min_u32(nextEventCycles, wave->timeHighCycles);
519+
} else {
520+
ClearGPIO(wave->gpioMask);
521+
if (wave->gpio16Mask) {
522+
GP16O &= ~wave->gpio16Mask;
523+
}
524+
wave->nextServiceCycle = now + wave->timeLowCycles;
525+
wave->timeLowCycles = wave->nextTimeLowCycles;
526+
nextEventCycles = min_u32(nextEventCycles, wave->timeLowCycles);
527+
}
528+
} else {
529+
uint32_t deltaCycles = wave->nextServiceCycle - now;
530+
nextEventCycles = min_u32(nextEventCycles, deltaCycles);
531+
}
532+
}
533+
} while (--cnt && (nextEventCycles < MicrosecondsToCycles(4)));
534+
466535
uint32_t curCycleCount = GetCycleCount();
467536
uint32_t deltaCycles = curCycleCount - lastCycleCount;
468537
lastCycleCount = curCycleCount;
469538

539+
// Check for timed-out waveforms out of the high-frequency toggle loop
470540
for (size_t i = 0; i < countof(waveform); i++) {
471541
Waveform *wave = &waveform[i];
472-
473-
// If it's not on, ignore!
474-
if (!wave->enabled) {
475-
continue;
476-
}
477-
478-
// Check for timed-out waveforms
479542
if (wave->timeLeftCycles) {
480-
uint32_t newTimeLeftCycles = wave->timeLeftCycles - deltaCycles;
481543
// Check for unsigned underflow with new > old
482-
if ((deltaCycles >= wave->timeLeftCycles) || (newTimeLeftCycles <= CYCLES_FLUFF)) {
544+
if (deltaCycles >= wave->timeLeftCycles) {
483545
// Done, remove!
484546
wave->enabled = false;
485-
if (wave->gpioPin16) {
486-
GP16O &= ~1;
487-
} else {
488-
ClearGPIO(1 << wave->gpioPin);
489-
}
547+
ClearGPIO(wave->gpioMask);
548+
GP16O &= ~wave->gpio16Mask;
490549
} else {
550+
uint32_t newTimeLeftCycles = wave->timeLeftCycles - deltaCycles;
491551
wave->timeLeftCycles = newTimeLeftCycles;
492552
}
493553
}
494-
495-
// Check for toggles
496-
uint32_t newNextEventCycles = wave->nextEventCycles - deltaCycles;
497-
if ((deltaCycles >= wave->nextEventCycles) || (newNextEventCycles <= CYCLES_FLUFF)) {
498-
wave->state = !wave->state;
499-
if (wave->state) {
500-
if (wave->gpioPin16) {
501-
GP16O |= 1;
502-
} else {
503-
SetGPIO(1 << wave->gpioPin);
504-
}
505-
wave->nextEventCycles = wave->timeHighCycles;
506-
wave->timeHighCycles = wave->nextTimeHighCycles;
507-
} else {
508-
if (wave->gpioPin16) {
509-
GP16O &= ~1;
510-
} else {
511-
ClearGPIO(1 << wave->gpioPin);
512-
}
513-
wave->nextEventCycles = wave->timeLowCycles;
514-
wave->timeLowCycles = wave->nextTimeLowCycles;
515-
}
516-
} else {
517-
wave->nextEventCycles = newNextEventCycles;
518-
}
519-
}
520-
521-
uint32_t nextEventCycles = MicrosecondsToCycles(MAXIRQUS);
522-
for (size_t i = 0; i < countof(waveform); i++) {
523-
if (waveform[i].enabled) {
524-
nextEventCycles = min_u32(nextEventCycles, waveform[i].nextEventCycles);
525-
}
526554
}
527555

528556
if (stepQCnt) {
529557
nextEventCycles = min_u32(nextEventCycles, ProcessSteppers(deltaCycles));
530558
}
531559

532-
// Adjust back by the time we spent in here
533-
deltaCycles = GetCycleCount() - lastCycleCount;
534-
// Add in IRQ delay, from measurements on idle system
535560
#if F_CPU == 160000000
536-
deltaCycles += MicrosecondsToCycles(1) + (MicrosecondsToCycles(1) >> 1);
537-
#else
538-
deltaCycles += MicrosecondsToCycles(3);
539-
#endif
540-
if (nextEventCycles > deltaCycles) {
541-
nextEventCycles -= deltaCycles;
561+
if (nextEventCycles <= 5 * MicrosecondsToCycles(1)) {
562+
nextEventCycles = MicrosecondsToCycles(1) / 2;
542563
} else {
543-
nextEventCycles = CYCLES_FLUFF;
564+
nextEventCycles -= 5 * MicrosecondsToCycles(1);
544565
}
545-
546-
// Keep next call within sane min/max time
547-
if (nextEventCycles < CYCLES_FLUFF) {
548-
nextEventCycles = CYCLES_FLUFF;
549-
} else if (nextEventCycles > MicrosecondsToCycles(MAXIRQUS)) {
550-
nextEventCycles = MicrosecondsToCycles(MAXIRQUS);
551-
}
552-
#if F_CPU == 160000000
553566
nextEventCycles = nextEventCycles >> 1;
567+
#else
568+
if (nextEventCycles <= 6 * MicrosecondsToCycles(1)) {
569+
nextEventCycles = MicrosecondsToCycles(1) / 2;
570+
} else {
571+
nextEventCycles -= 6 * MicrosecondsToCycles(1);
572+
}
554573
#endif
574+
555575
ReloadTimer(nextEventCycles);
556576
}
557577

cores/esp8266/core_esp8266_waveform.h

+3-3
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ extern "C" {
6464
int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t runTimeUS);
6565
int stopWaveform(uint8_t pin);
6666

67-
int setStepperDirPin(int pin);
68-
int pushStepperMove(int pin, int dir, int sync, uint16_t pulses, float j, float a0, float v0);
69-
int removeStepper(int pin);
67+
int setStepperDirPin(uint8_t pin);
68+
int pushStepperMove(uint8_t pin, int dir, int sync, uint16_t pulses, float j, float a0, float v0);
69+
int removeStepper(uint8_t pin);
7070

7171
#ifdef __cplusplus
7272
}

0 commit comments

Comments
 (0)