EOS R: HDR exposure bracketing + manual shutter control - #280
Conversation
Enables working exposure (HDR) bracketing on the EOS R. The key fix is the R's PROP_SHUTTER encoding: it is a 2-byte value whose high byte is the shutter as a signed Tv in 1/3-stop units (0 = 1"). ML had been writing the value in the low byte, so the camera always saw Tv 0 and every frame came out 1". lens.c now encodes/decodes PROP_SHUTTER at the property boundary, keeping ML's internal raw scale unchanged. - lens.c: hi-byte-Tv encode/decode (ml_raw_to_r_shutter16 / r_shutter16_to_ml_raw); CONFIG_R capture via the CameraConductor remote-release event (SetEventIrRemoteReleaseBtn, 0xE0190215) with an early return so bracket frames sequence cleanly; coerce out-of-range shutter writes to the camera limit. - shoot.c: detect the user's shot via the card file_number (the R's job_state notifications are unreliable) to fire the bracket; read the M-mode dial via a brief half-press so the bracket centers on the user's shutter; drop the MENU_MODE cancel check on R; treat auto frame count as a fixed 5-frame symmetric bracket. - features.h: enable HDR bracketing + intervalometer for R.180. - property_whitelist.h: allow PROP_SHUTTER. Tested on EOS R (FW 1.8.0): single capture (clean finalize), 5-frame HDR bracket centered on the dial with varying exposures, intervalometer, and clean power-off.
Test matrix — EOS R, firmware 1.8.0 (7.3.9)All run on hardware. Setup: M mode, manual focus, scene with tonal range.
Net: single capture, correctly-centered 5-frame HDR bracket with varying exposures (verified across the 1/8000–30" range), intervalometer, and clean power-off all working. |
|
Re-ran a smoke test on the final stripped branch build (the exact PR content, with the dev probes removed): single capture + clean power-off, 5-frame HDR bracket (centered on the dial, exposures stepping dark→bright), and intervalometer all confirmed working on the EOS R. No behavioral change from the matrix above. |
|
Prefer this PR over #279 |
Gate the R shutter-encoding and HDR-bracket adaptations on semantic capability flags (CONFIG_PROP_SHUTTER_HI_BYTE, CONFIG_HDR_BRACKET_VIA_FILENUMBER) defined in platform/R.180/internals.h, instead of scattered CONFIG_R. Also: remove a leftover BRKCANCEL.TXT debug log, restore the shared prop_set_rawshutter length (the R uses its own path), and use a named SetEventIrRemoteReleaseBtn stub instead of a raw address.
|
Pushed a follow-up to reduce the R special-casing:
Behavior-preserving for the R (the flags compile the same blocks); builds clean under -Werror. |
The R's PROP_ISO is a 4-byte value with the ISO code in byte 1 (val = code<<8), code 15 = ISO 100, +3 per stop -- the same high-byte trick PROP_SHUTTER uses. The R also broadcasts PROP_ISO on the dial, so read works once the handler decodes byte 1. Mirrors the CONFIG_PROP_SHUTTER_HI_BYTE pattern. - internals.h: CONFIG_PROP_ISO_BYTE1 capability flag. - lens.c: ml_raw_to_r_iso / r_iso_to_ml_raw (ML APEX raw <-> R byte-1 code), r_set_rawiso; PROP_HANDLER(PROP_ISO) decodes byte 1; prop_set_rawiso routes through r_set_rawiso under the flag. - property_whitelist.h: PROP_ISO out of prop_handler_deny[] (read + length cache) and into prop_write_allow[] (write). Validated on an EOS R (FW 1.8.0): boots with PROP_ISO un-denied, ML reads the dial ISO, and lens_set_rawiso sets ISO 100/400/800/3200 with exact read-back.
The R's PROP_APERTURE is a 2-byte value with the Av code in byte 1 (val = code<<8), 3 per stop: code 9=f/2.8, 18=f/8, 27=f/22 = round((ml_raw-8)*3/8) -- the same byte-1 / *3/8 family as PROP_ISO (only the zero-offset differs). The R also broadcasts PROP_APERTURE on the dial, so read works once the handler decodes byte 1; writing byte 1 drives the lens and the R clamps to the lens range itself. - internals.h: CONFIG_PROP_APERTURE_BYTE1 capability flag. - lens.c: ml_raw_to_r_aperture / r_aperture_to_ml_raw + r_set_rawaperture (write code<<8, len 2, no min/max coerce -- raw_aperture_min/max are 0 on the R, so the generic COERCE would clamp to [0,0]); PROP_HANDLER(PROP_APERTURE) decodes byte 1; prop_set_rawaperture routes through r_set_rawaperture under the flag. - property_whitelist.h: PROP_APERTURE added to prop_write_allow (read handler was already registered -- not deny-listed). Validated on an EOS R (FW 1.8.0): read tracks the dial, and lens_set_rawaperture sets f/4/f/8/f/11 with exact read-back (APSET: raw 40/56/64 -> readback 40/56/64).
|
Expanded this PR to cover all three manual exposure controls on the R, not just shutter — ISO and aperture read/write now work too. The interesting part: the R encodes all three exposure properties the same unusual way. The value sits in a high byte as an offset code at 3 units per stop, rather than ML's usual low-byte APEX raw — which is why naïve writes silently did nothing (ISO/aperture stuck at one value) or came out as a fixed 1" exposure (shutter):
ML keeps its own APEX raw scale internally and converts only at the property boundary ( Validated on an EOS R (FW 1.8.0): all three track the dial correctly and set correctly through the production |
These were one-time reverse-engineering tools to map the R's exposure-property encodings. All three props (shutter, ISO, aperture) are now validated and the features are on PR reticulatedpines#280, so the probes have served their purpose. Removing them (scale map / write sweep / format test / dial read / hi-byte map / read map / set test, plus the runtime PROP_ISO and PROP_APERTURE probe slaves) drops _bss_end from 0x143600 to 0x13f600 -- ~16KB reclaimed, restoring ~20KB of user_mem budget margin (the probe build had been ~20 bytes over -> red LED). Kept: the SF capture/active-read probes (Path 2) and the other capture probes.
What this does
Enables working exposure (HDR) bracketing on the EOS R (FW 1.8.0), plus the shutter-control and capture pieces it needs.
The core fix: PROP_SHUTTER encoding
On the R,
PROP_SHUTTERis a 2-byte value whose high byte is the shutter as a signed Tv in 1/3-stop units (0 = 1", +3 = 0.5", -3 = 2", verified on camera). ML had been writing the value in the low byte, so the camera always saw Tv 0 and every frame came out 1" — which is why bracketing produced no exposure variation. This converts at the property boundary (write and read), leaving ML's internal raw scale unchanged.Included
job_statenotifications are unreliable) to fire the bracket; read the M-mode dial via a brief half-press so the bracket centers on the user's shutter; drop theMENU_MODEcancel check on R; auto frame count → fixed 5-frame symmetric bracket.Comparison with #279, and why this one
#279 fixes the single-capture "saving…" hang on the R using the same CameraConductor remote-release event. This PR carries an equivalent capture fix and goes further:
So this is a superset: an equivalent remote-release capture fix, plus the early-return refinement and the shutter-encoding fix that multi-frame bracketing requires. It can replace #279, or be rebased onto it if you'd rather land #279 first — happy to do whichever.
(Minor: #279 wires the remote-release as a named stub
SetEventIrRemoteReleaseBtn; this PR currently calls it by address0xE0190215with a comment — trivial to switch to the named stub if you prefer.)Testing
Validated on an EOS R (FW 1.8.0) — full functional matrix in the comment below (single capture, 5-frame HDR bracket centered on the dial with varying exposures, intervalometer, clean power-off).
Known limitations