cp_watchdog_timing: do not starve the controller (no nice, sleep per pass), two jogs 4 s apart

This commit is contained in:
ScottW514
2026-08-15 13:54:48 -04:00
parent fe6a65b7fe
commit 52b398e11d
+23 -15
View File
@@ -11,11 +11,13 @@ SoC's own pins, with no kernel change and no scope:
The sampler arms rising-edge detection for GPIO3 pin 24 only (ICR2 bits The sampler arms rising-edge detection for GPIO3 pin 24 only (ICR2 bits
17:16), clears that pin's sticky ISR flag, then polls ISR bit 24 and the two 17:16), clears that pin's sticky ISR flag, then polls ISR bit 24 and the two
readback pads in a tight loop, restoring ICR2 on exit. GPIO3 IMR bit 24 must readback pads in a loop with a ~100 us sleep per pass, restoring ICR2 on
be clear (nothing has an interrupt on that pin; the run refuses otherwise), exit. GPIO3 IMR bit 24 must be clear (nothing has an interrupt on that pin;
so the kernel never sees the flag. Reads and writes go through /dev/mem; the run refuses otherwise), so the kernel never sees the flag. Reads and
the CHG_PUMP pad has SION set in the device tree, so its pad state is writes go through /dev/mem; the CHG_PUMP pad has SION set in the device
visible. tree, so its pad state is visible. The loop must not hog the CPU: only the
controller's shipper thread is SCHED_FIFO, its protocol/producer thread is
SCHED_OTHER, and a busy loop starves it so a run never ends.
Every run of the pulse engine (a jog is enough) primes the pump once and Every run of the pulse engine (a jog is enough) primes the pump once and
then feeds it every 200 ms while `state == running`; when the run ends the then feeds it every 200 ms while `state == running`; when the run ends the
@@ -26,9 +28,9 @@ count and period. Resolution = the loop period (tens of microseconds; the
worst gap is printed). worst gap is printed).
Usage: cp_watchdog_timing.py [seconds] [jog ...] Usage: cp_watchdog_timing.py [seconds] [jog ...]
default: 16 s, jogs "$J=G91 X5 F300" "$J=G91 X-5 F300" x2 (out and back default: 14 s, jogs "$J=G91 X5 F300" "$J=G91 X-5 F300" (out and back,
twice, 5 mm, head ends where it started), sent to the local grblHAL on 5 mm, head ends where it started), sent to the local grblHAL on
127.0.0.1:23 at t = 2, 4.6, 7.2, 9.8 s. Pass "-" as the only jog to sample 127.0.0.1:23 at t = 2 s and 6 s. Pass "-" as the only jog to sample
without commanding motion (drive the runs yourself). without commanding motion (drive the runs yourself).
Motion only, laser locked; needs the GRBL controller idle with no other Motion only, laser locked; needs the GRBL controller idle with no other
Grbl client attached (a connection here displaces the sender). Grbl client attached (a connection here displaces the sender).
@@ -39,13 +41,14 @@ GPIO1, GPIO3, GPIO4 = 0x0209C000, 0x020A4000, 0x020A8000
PSR, ICR2, IMR, ISR = 0x08, 0x10, 0x14, 0x18 PSR, ICR2, IMR, ISR = 0x08, 0x10, 0x14, 0x18
PULSE_PIN = 24 # GPIO3_24 CHG_PUMP PULSE_PIN = 24 # GPIO3_24 CHG_PUMP
NQ_BIT, NHV_BIT = 8, 6 # GPIO1_08 !Q, GPIO4_06 !HV_ENABLE NQ_BIT, NHV_BIT = 8, 6 # GPIO1_08 !Q, GPIO4_06 !HV_ENABLE
JOG_T0, JOG_DT = 2.0, 2.6 JOG_T0, JOG_DT = 2.0, 4.0
SLEEP_S = 0.0001
DEFAULT_JOGS = ['$J=G91 X5 F300', '$J=G91 X-5 F300'] * 2 DEFAULT_JOGS = ['$J=G91 X5 F300', '$J=G91 X-5 F300']
def main(): def main():
dur = float(sys.argv[1]) if len(sys.argv) > 1 else 16.0 dur = float(sys.argv[1]) if len(sys.argv) > 1 else 14.0
jogs = sys.argv[2:] if len(sys.argv) > 2 else DEFAULT_JOGS jogs = sys.argv[2:] if len(sys.argv) > 2 else DEFAULT_JOGS
if jogs == ['-']: if jogs == ['-']:
jogs = [] jogs = []
@@ -66,10 +69,13 @@ def main():
if rd(g3, IMR) & (1 << PULSE_PIN): if rd(g3, IMR) & (1 << PULSE_PIN):
print('ABORT: GPIO3 IMR bit %d is set - something has an interrupt on the pump pin' % PULSE_PIN) print('ABORT: GPIO3 IMR bit %d is set - something has an interrupt on the pump pin' % PULSE_PIN)
return 2 return 2
try:
os.nice(-20) def cnc_state():
except OSError: try:
pass with open('/sys/glowforge/cnc/state') as f:
return f.read().strip()
except OSError:
return '?'
sock = None sock = None
if jogs: if jogs:
@@ -109,6 +115,7 @@ def main():
if nq != nq_prev: if nq != nq_prev:
events.append((t, '!Q->%d' % nq)) events.append((t, '!Q->%d' % nq))
nq_prev = nq nq_prev = nq
events.append((time.monotonic_ns(), 'cnc/state=' + cnc_state()))
if nhv != nhv_prev: if nhv != nhv_prev:
events.append((t, '!HV->%d' % nhv)) events.append((t, '!HV->%d' % nhv))
nhv_prev = nhv nhv_prev = nhv
@@ -123,6 +130,7 @@ def main():
sent += 1 sent += 1
if t > tend: if t > tend:
break break
time.sleep(SLEEP_S)
finally: finally:
wr(g3, ICR2, icr2_orig) wr(g3, ICR2, icr2_orig)
wr(g3, ISR, 1 << PULSE_PIN) wr(g3, ISR, 1 << PULSE_PIN)