Skip to content

openipc_frame_ts: strobe-out — drive a GPIO synchronously with frame events#179

Merged
widgetii merged 1 commit into
mainfrom
feat/openipc-frame-ts-strobe-out
May 23, 2026
Merged

openipc_frame_ts: strobe-out — drive a GPIO synchronously with frame events#179
widgetii merged 1 commit into
mainfrom
feat/openipc-frame-ts-strobe-out

Conversation

@widgetii
Copy link
Copy Markdown
Member

Summary

Adds a strobe-out feature to the existing openipc_frame_ts module: drive a SoC GPIO at the exact moment the sensor begins streaming row 0 (MIPI_FS) and/or finishes the last row (ISP_FEND). The expensive part — being inside the kernel at sub-µs precision relative to vsync — already exists from PR #178. This PR adds ~260 lines so that hook can also flip a GPIO without spinning up a userspace daemon.

Two modes:

Mode Behaviour Use case
pulse Assert on MIPI_FS, deassert via hrtimer after strobe_pulse_us µs Flash / strobe light aligned to sensor row-0 start
hold Assert on MIPI_FS, deassert on ISP_FEND Ring light that should be on only while pixels are being received (high window = sensor readout duration, ~10–30 ms depending on mode)
off No GPIO activity Default

Configured via sysfs:

/sys/class/misc/openipc-frame-ts/
├── strobe_gpio         (rw int)   GPIO number, -1 = off (default)
├── strobe_mode         (rw str)   off | pulse | hold
├── strobe_pulse_us     (rw uint)  pulse width µs, 1..100000 (default 1000)
├── strobe_active_low   (rw bool)  invert polarity (default 0)
└── strobe_events       (r  u64)   counter of fires since module load

Constraints

  • The GPIO must be SoC-direct (memory-mapped). GPIOs that can sleep (I2C / SPI expanders) are rejected at configure time — the firing path is hardirq.
  • One global strobe slot for now; per-channel can be added later by indexing the state by vi_chn if anyone needs it.
  • Latency from sensor vsync to GPIO assert: a few µs. Pulse-width precision: hrtimer-grade.
  • Pure software strobe — physical GPIO must be exposed on the camera board (most IP-camera designs have at least one — the IR-cut filter / day-night sensor pin).

Test plan — validated on hardware

Both lab boards re-flashed with the new module:

ev300 (V4, IMX335)/tmp/strobe_stress.sh:

  • 10 s of pulse mode at strobe_pulse_us=50: 276 strobe_events fired
  • Invalid inputs rejected: gpio=9999, pulse_us=0, mode=bogus all returned EINVAL
  • dmesg clean: no relevant kernel messages
  • echo -1 > strobe_gpio cleanly releases the GPIO

av300 (cv500, IMX415):

  • 5 s of pulse: 56 events
  • Switch to hold mode while running: another 75 events in 5 s
  • Clean teardown

Why bother

The robotics-camera audience documented in the new wiki page (proposed here — not yet merged) lists "hardware trigger / strobe out" as one of the few capabilities that genuinely separate sub-$50 OpenIPC cameras from $500+ industrial gear. With this module the strobe-out half of that gap closes:

  • LED ring driven only during exposure (saves power, reduces heat, removes ambient artefacts)
  • Camera–IMU calibration: hardware-correlated pulse on every frame
  • Triggering downstream measurement equipment at frame rate
  • Active-illumination perception (laser line, structured light) gated by the camera's sensor window rather than a free-running timer

Trigger-IN (camera waits for an external pulse before each frame) is a separate, harder problem requiring per-sensor slave-mode driver work; out of scope for this PR.

🤖 Generated with Claude Code

widgetii pushed a commit that referenced this pull request May 23, 2026
…PIs)

Two modern-kernel API changes broke the strobe-out build (PR #179) on
the neo 6.6/6.18/7.0 lanes while legacy 4.9 was clean:

- hrtimer_init() + manual `timer->function = ...` was unified into a
  single hrtimer_setup(timer, fn, clock, mode) call in Linux 6.7 to
  close a race where the timer could fire before the function pointer
  was assigned. Add a forward-compat hrtimer_setup() inline in
  kernel/compat/kernel_compat.h for pre-6.7 kernels, and rewrite the
  init in openipc_frame_ts to use the new style.

- gpio_cansleep() — the int-based legacy wrapper — was removed during
  the gpiolib cleanups around Linux 6.5+ in favour of the
  descriptor-based gpiod_cansleep(). The rest of the legacy int GPIO
  API (gpio_request / gpio_set_value / gpio_direction_output) is still
  available via CONFIG_GPIOLIB. Bridge through gpio_to_desc() in the
  compat header so driver code keeps the int-handle calling convention.

Both shims are guarded by LINUX_VERSION_CODE in kernel_compat.h —
driver code stays version-agnostic per the project rule.

Verified: legacy 4.9.37 builds clean on ev300 + smoke-tested on
hardware (strobe_events incrementing at sensor rate, invalid inputs
rejected, dmesg clean).
…events

The existing ISR hook in mipi_rx_interrupt_route / ISP_ISR already knows
the exact moment the sensor begins streaming row 0 (MIPI_FS) and the
moment the ISP front-end finished receiving the last row (ISP_FEND).
This patch lets that hook also drive a SoC GPIO at those moments, for
triggering external illumination / measurement equipment without
needing a separate userspace daemon and without userspace scheduling
jitter.

Two modes:

  pulse — assert on MIPI_FS, deassert via hrtimer N µs later. For
          flash / strobe lights wanting a fixed-width pulse aligned
          to sensor row-0 start.

  hold  — assert on MIPI_FS, deassert on ISP_FEND. High window equals
          the sensor readout duration (~10–30 ms depending on mode).
          For a ring light that should be on only while pixels are
          being received.

Configured at runtime via sysfs on the misc device:

  /sys/class/misc/openipc-frame-ts/
    strobe_gpio         (rw int)   GPIO number, -1 = off (default)
    strobe_mode         (rw str)   off | pulse | hold
    strobe_pulse_us     (rw uint)  pulse width µs, 1..100000 (default 1000)
    strobe_active_low   (rw bool)  invert polarity
    strobe_events       (r  u64)   counter of fires

GPIOs that can sleep (I2C / SPI expanders) are rejected at configure
time — the firing path runs in hardirq context. One global strobe
slot for now; per-channel could be added later by indexing the state
by vi_chn.

Latency from sensor vsync to GPIO assert: a few µs (kernel IRQ entry
plus register write). Pulse-width precision: hrtimer-grade.

Cross-kernel: hrtimer_setup() (Linux 6.7+) is provided as a macro
shim in kernel_compat.h for older trees, expanding to
hrtimer_init() + manual function assignment. Macro rather than
static inline to avoid pulling <linux/hrtimer.h> into every TU via
the `-include`d compat header.

Hardware-validated on ev300 (V4 / IMX335) and av300 (cv500 / IMX415):

  echo 16 > strobe_gpio
  echo 50 > strobe_pulse_us
  echo pulse > strobe_mode
  # 10 s @ ~30 fps sensor: 276 strobe_events fired, dmesg clean,
  # invalid inputs rejected, gpio cleanly released on -1
  echo hold > strobe_mode
  # FS→FEND hold confirmed; events count matches frame rate
@widgetii widgetii force-pushed the feat/openipc-frame-ts-strobe-out branch from a6b7090 to 1b1859b Compare May 23, 2026 18:31
@widgetii widgetii merged commit baf3d9d into main May 23, 2026
30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant