Add calibration patterns and a geometric note-to-LED map
Hardware arrives tomorrow, which makes this the blocking work: the appliance has no console, so without it there is no way to answer "is pixel 0 at the end I think it is" except by guessing. Geometric mapping. NOTE_MAP_GEOMETRIC (default on) derives each key from white-key geometry rather than semitone index: 52 white keys span the strip, so a white key is LED_COUNT/52 pixels - about 3.38 at 176 LEDs, not 2 - with black keys on the boundaries. The old linear map drifts within each octave, worst at F, by up to ~0.87 LEDs (~6mm) even after an optimal offset and scale. Set NOTE_MAP_GEOMETRIC=0 to restore it. This exposed a real bug. Under the geometric map adjacent key spans overlap, because the semitone pitch (~1.7 LEDs) is narrower than LEDS_PER_KEY. The renderer painted unlit keys black, so a key erased its lit neighbour's pixels. It now clears once and paints only lit keys. The linear map never overlapped, so this could not have been found without the geometry change. LED_OFFSET shifts every key, absorbing where the strip was actually cut and where the profile ended up. Off-strip pixels are clipped, never wrapped. Calibration patterns, selected by CC 20, with CC 21/22 setting the pixel for the walk: ends (orientation and length), octaves (mapping drift), keys (whole mapping at once), walk (finding LED_OFFSET), all (voltage droop at the far end). Patterns run at the same brightness ceiling as normal operation, so none can exceed the current budget the design already allows. tools/calibrate.sh drives all of it from the PC over ALSA MIDI, and README carries the six-step procedure in dependency order. Verified: tests pass across fourteen configurations, now including both mapping modes and positive, negative and reversed offsets. Both platforms build clean - pianoled.uf2 for RP2350 and both Circle kernel images - with no warnings from project sources. Claude-Session: https://claude.ai/code/session_01TVCB25LBsmeteWvaSMz4Ne
This commit is contained in:
@@ -41,6 +41,34 @@
|
||||
#define STRIP_REVERSED 0
|
||||
#endif
|
||||
|
||||
// Global shift, in pixels, applied after mapping. Absorbs where the strip was
|
||||
// actually cut and where the profile ended up on the instrument - things the
|
||||
// geometry cannot know. Positive moves every key towards higher pixel indices.
|
||||
// Find it with calibration pattern 4 (single-LED walk); see README.
|
||||
#ifndef LED_OFFSET
|
||||
#define LED_OFFSET 0
|
||||
#endif
|
||||
|
||||
// Note-to-LED mapping.
|
||||
//
|
||||
// 1 = geometric. Derives each key's position from white-key geometry:
|
||||
// 52 white keys span the strip, so one white key is LED_COUNT/52
|
||||
// pixels (~3.38 at 176 LEDs) with black keys on the boundaries.
|
||||
// 0 = linear. The plan's original (note - 21) * LEDS_PER_KEY.
|
||||
//
|
||||
// Linear is wrong on a real keybed, because semitones are not evenly spaced:
|
||||
// it drifts within each octave, worst at F, by up to ~0.87 LEDs (~6mm) even
|
||||
// after an optimal offset and scale. Geometric removes that. Keep linear only
|
||||
// to reproduce the original behaviour.
|
||||
#ifndef NOTE_MAP_GEOMETRIC
|
||||
#define NOTE_MAP_GEOMETRIC 1
|
||||
#endif
|
||||
|
||||
// Number of white keys spanned by the strip. 52 for a standard 88-key keybed.
|
||||
#ifndef WHITE_KEY_COUNT
|
||||
#define WHITE_KEY_COUNT 52
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Power safety (plan section 7) - NOT optional
|
||||
// --------------------------------------------------------------------------
|
||||
@@ -130,6 +158,38 @@
|
||||
#define HINT_MIDI_CHANNEL 15 // channel 16 in a DAW
|
||||
#endif
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Calibration (plan Phase 0)
|
||||
// --------------------------------------------------------------------------
|
||||
//
|
||||
// This is a headless appliance with no console, so calibration is driven over
|
||||
// MIDI - the one channel that already exists. Send these CCs from the PC; see
|
||||
// tools/calibrate.sh.
|
||||
//
|
||||
// Patterns are a diagnostic overlay: while one is active it replaces the note
|
||||
// display entirely, and pattern 0 restores normal operation.
|
||||
|
||||
// CC selecting the active pattern.
|
||||
#ifndef CALIB_CC_PATTERN
|
||||
#define CALIB_CC_PATTERN 20
|
||||
#endif
|
||||
|
||||
// CCs setting the pixel index for CALIB_PATTERN_WALK, as a 14-bit value:
|
||||
// index = (CC21 << 7) | CC22.
|
||||
#ifndef CALIB_CC_INDEX_HI
|
||||
#define CALIB_CC_INDEX_HI 21
|
||||
#endif
|
||||
#ifndef CALIB_CC_INDEX_LO
|
||||
#define CALIB_CC_INDEX_LO 22
|
||||
#endif
|
||||
|
||||
#define CALIB_PATTERN_OFF 0 // normal operation
|
||||
#define CALIB_PATTERN_ENDS 1 // first and last pixel only
|
||||
#define CALIB_PATTERN_OCTAVES 2 // every C, to expose mapping drift
|
||||
#define CALIB_PATTERN_KEYS 3 // every key, alternating colour
|
||||
#define CALIB_PATTERN_WALK 4 // one pixel, chosen by CC21/CC22
|
||||
#define CALIB_PATTERN_ALL 5 // every pixel, for voltage droop testing
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
// Hardware wiring - platform specific
|
||||
// --------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user