Skip to content

Commit 330f9cd

Browse files
Save 88 bytes, make 1us period exact, allow GP16 stepper
Remove unneeded curTimeHigh/curTimeLow from the waveform structure, saving 88 bytes (11 pins x 8bytes) of heap. Also reduces inner loop in the IRQ by an couple stores. Also add measured offset to the waveform generation request, enabling a 1/1000 reqest to produce a 1.083us high pulse, and a 999/1000 to give a 1.083 low period, and the cycle time to be exactly 1.00ms over the range. IRQ doesn't need to be disabled on waveform changes as the code order between the irq and start/stop is safe. Allow GPIO16 as a stepper out pin
1 parent 2b1c0f0 commit 330f9cd

File tree

1 file changed

+53
-59
lines changed

1 file changed

+53
-59
lines changed

cores/esp8266/core_esp8266_waveform.c

+53-59
Original file line numberDiff line numberDiff line change
@@ -80,35 +80,30 @@
8080

8181
// Waveform generator can create tones, PWM, and servos
8282
typedef struct {
83-
uint32_t nextServiceCycle;
84-
uint32_t timeHighCycles;
85-
uint32_t timeLowCycles;
86-
uint32_t timeLeftCycles;
87-
// To ensure stable change, only copy these over on low->high transition
88-
uint16_t gpioMask;
89-
uint16_t gpio16Mask;
90-
// unsigned gpioPin : 4; // Check gpioPin16 first
91-
unsigned state : 1;
92-
unsigned nextTimeHighCycles : 31;
93-
// unsigned gpioPin16 : 1; // Special case for weird IO16
94-
unsigned enabled : 1;
95-
unsigned nextTimeLowCycles : 31;
83+
uint32_t nextServiceCycle; // ESP cycle timer when a transition required
84+
uint32_t timeLeftCycles; // For time-limited waveform, how many ESP cycles left
85+
uint16_t gpioMask; // Mask instead of value to speed IRQ loop
86+
uint16_t gpio16Mask; // Mask instead of value to speed IRQ loop
87+
unsigned state : 1; // Current state of this pin
88+
unsigned nextTimeHighCycles : 31; // Copy over low->high to keep smooth waveform
89+
unsigned enabled : 1; // Is this GPIO generating a waveform?
90+
unsigned nextTimeLowCycles : 31; // Copy over high->low to keep smooth waveform
9691
} Waveform;
9792

9893
// These can be accessed in interrupts, so ensure to bracket access with SEI/CLI
9994
static Waveform waveform[] = {
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},
95+
{0, 0, 1<<0, 0, 0, 0, 0, 0}, // GPIO0
96+
{0, 0, 1<<1, 0, 0, 0, 0, 0}, // GPIO1
97+
{0, 0, 1<<2, 0, 0, 0, 0, 0},
98+
{0, 0, 1<<3, 0, 0, 0, 0, 0},
99+
{0, 0, 1<<4, 0, 0, 0, 0, 0},
100+
{0, 0, 1<<5, 0, 0, 0, 0, 0},
106101
// GPIOS 6-11 not allowed, used for flash
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
102+
{0, 0, 1<<12, 0, 0, 0, 0, 0},
103+
{0, 0, 1<<13, 0, 0, 0, 0, 0},
104+
{0, 0, 1<<14, 0, 0, 0, 0, 0},
105+
{0, 0, 1<<15, 0, 0, 0, 0, 0},
106+
{0, 0, 0, 1, 0, 0, 0, 0} // GPIO16
112107
};
113108

114109

@@ -137,20 +132,20 @@ typedef struct {
137132
uint32_t nextEventCycles;
138133

139134
// Copied from head for fast access
140-
uint16_t pulses;
141-
uint32_t cumCycles;
142-
float j_2;
143-
float a0;
144-
float v0;
145-
unsigned sync : 1;
146-
unsigned dir : 1;
147-
148-
unsigned gpioPin : 4;
149-
unsigned finished : 1;
150-
151-
uint8_t readPtr;
152-
uint8_t writePtr;
153-
uint8_t validEntries;
135+
uint16_t pulses; // Pulses remaining
136+
uint32_t cumCycles; // The "t" in our equations
137+
float j_2; // j/2 (jerk divided by 2.0)
138+
float a0; // Initial constant acceleration
139+
float v0; // Initial constant velocity
140+
unsigned sync : 1; // Wait for all steppers to finish before advancing
141+
unsigned dir : 1; // CCW or CW
142+
143+
unsigned finished : 1; // Done with all moves, on next hit pop another motion
144+
unsigned gpioPin : 5; // Allow all GPIOs, we're going to be slow no matter what
145+
146+
uint8_t readPtr; // Read queue index
147+
uint8_t writePtr; // Push queue spot
148+
uint8_t validEntries; // How many entries present
154149
} StepperQueue;
155150

156151
static volatile StepperQueue *stepQ = NULL;
@@ -254,9 +249,10 @@ int removeStepper(uint8_t pin) {
254249

255250
// Add a stepper move, return TRUE on success, FALSE on out of space
256251
// Calling application needs to ensure IRQS are disabled for the call!
257-
static int PushStepper(int gpioPin, const Motion *nextMove) {
252+
static int PushStepper(uint8_t gpioPin, const Motion *nextMove) {
258253
StepperQueue *q = NULL;
259254
int i;
255+
// gpioPin already validated in calling function
260256
sei();
261257
// Determine which queue it should be on, or maybe add one if needed
262258
for (i = 0; i < stepQCnt; i++) {
@@ -314,10 +310,10 @@ static int PushStepper(int gpioPin, const Motion *nextMove) {
314310

315311
// Called by user to add a PWL move to the queue, returns false if there is no space left
316312
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
313+
if (pin > 16) {
319314
return false;
320315
}
316+
321317
Motion m;
322318
m.pulses = pulses;
323319
m.j_2 = j / 2.0;
@@ -351,31 +347,26 @@ int startWaveform(uint8_t pin, uint32_t timeHighUS, uint32_t timeLowUS, uint32_t
351347
if (!wave) {
352348
return false;
353349
}
354-
sei();
355-
wave->nextTimeHighCycles = MicrosecondsToCycles(timeHighUS);
356-
wave->nextTimeLowCycles = MicrosecondsToCycles(timeLowUS);
350+
wave->nextTimeHighCycles = MicrosecondsToCycles(timeHighUS) - 70; // Take out some time for IRQ codepath
351+
wave->nextTimeLowCycles = MicrosecondsToCycles(timeLowUS) - 70; // Take out some time for IRQ codepath
357352
wave->timeLeftCycles = MicrosecondsToCycles(runTimeUS);
358353
if (!wave->enabled) {
359354
wave->state = 0;
360355
// 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
363356
wave->nextServiceCycle = GetCycleCount() + MicrosecondsToCycles(1);
364357
wave->enabled = 1;
365358
if (!timerRunning) {
366359
initTimer();
367360
}
368361
ReloadTimer(MicrosecondsToCycles(1)); // Cause an interrupt post-haste
369362
}
370-
cli();
371363
return true;
372364
}
373365

374366
// Stops a waveform on a pin
375367
int stopWaveform(uint8_t pin) {
376368
for (size_t i = 0; i < countof(waveform); i++) {
377369
if (((pin == 16) && waveform[i].gpio16Mask) || ((pin != 16) && (waveform[i].gpioMask == 1<<pin))) {
378-
sei();
379370
waveform[i].enabled = 0;
380371
int cnt = stepQCnt;
381372
for (size_t i = 0; i < countof(waveform); i++) {
@@ -384,7 +375,6 @@ int stopWaveform(uint8_t pin) {
384375
if (!cnt) {
385376
deinitTimer();
386377
}
387-
cli();
388378
return true;
389379
}
390380
}
@@ -397,8 +387,8 @@ int stopWaveform(uint8_t pin) {
397387
// Send pulses for specific direction.
398388
// Stepper direction pin needs to be set before calling (helps ensure setup time)
399389
static ICACHE_RAM_ATTR void AdvanceSteppers(uint32_t deltaCycles, int dir) {
400-
static uint16_t toClear = 0; // Store last call's pins to allow us to meet hold time by clearing on the processing of the other dir
401-
uint16_t pulseGPIO = 0;
390+
static uint32_t toClear = 0; // Store last call's pins to allow us to meet hold time by clearing on the processing of the other dir
391+
uint32_t pulseGPIO = 0;
402392
for (size_t i = 0; i < stepQCnt; i++) {
403393
StepperQueue *q = (StepperQueue*)&stepQ[i];
404394
if (q->dir != dir || q->finished) {
@@ -443,8 +433,14 @@ static ICACHE_RAM_ATTR void AdvanceSteppers(uint32_t deltaCycles, int dir) {
443433
q->nextEventCycles = newNextEventCycles;
444434
}
445435
}
446-
ClearGPIO(toClear);
447-
SetGPIO(pulseGPIO);
436+
ClearGPIO(toClear & 0xffff);
437+
if (toClear & 0x80000) {
438+
GP16O &= ~1; // RMW is slow, only do if needed
439+
}
440+
SetGPIO(pulseGPIO & 0xffff);
441+
if (pulseGPIO & 0x80000) {
442+
GP16O |= 1; // RMW is slow, only do if needed
443+
}
448444
toClear = pulseGPIO;
449445
}
450446

@@ -513,17 +509,15 @@ static ICACHE_RAM_ATTR void timer1Interrupt() {
513509
if (wave->gpio16Mask) {
514510
GP16O |= wave->gpio16Mask; // GPIO16 write slow as it's RMW
515511
}
516-
wave->nextServiceCycle = now + wave->timeHighCycles;
517-
wave->timeHighCycles = wave->nextTimeHighCycles;
518-
nextEventCycles = min_u32(nextEventCycles, wave->timeHighCycles);
512+
wave->nextServiceCycle = now + wave->nextTimeHighCycles;
513+
nextEventCycles = min_u32(nextEventCycles, wave->nextTimeHighCycles);
519514
} else {
520515
ClearGPIO(wave->gpioMask);
521516
if (wave->gpio16Mask) {
522517
GP16O &= ~wave->gpio16Mask;
523518
}
524-
wave->nextServiceCycle = now + wave->timeLowCycles;
525-
wave->timeLowCycles = wave->nextTimeLowCycles;
526-
nextEventCycles = min_u32(nextEventCycles, wave->timeLowCycles);
519+
wave->nextServiceCycle = now + wave->nextTimeLowCycles;
520+
nextEventCycles = min_u32(nextEventCycles, wave->nextTimeLowCycles);
527521
}
528522
} else {
529523
uint32_t deltaCycles = wave->nextServiceCycle - now;

0 commit comments

Comments
 (0)