pm-kit: minimal isolation (heap + parse + draw_ui, no inputs/audio)
Diagnosing the blank screen: strips everything but the heap init, one allocator exercise (parse), and the confirmed-working draw_ui. Blue+corners => heap/parse/ display fine, bug is the metronome loop. Black => heap/parse breaks the display. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
aeda999526
commit
9e2e833485
1 changed files with 8 additions and 129 deletions
|
|
@ -150,144 +150,23 @@ fn main() -> ! {
|
|||
.init(&mut timer)
|
||||
.unwrap();
|
||||
|
||||
// ---- inputs + speaker ----
|
||||
let mut btn_a = pins.gpio15.into_pull_up_input(); // A = play/stop
|
||||
let mut btn_b = pins.gpio14.into_pull_up_input(); // B = grid/notation view
|
||||
let mut adc = hal::adc::Adc::new(pac.ADC, &mut pac.RESETS);
|
||||
let mut joy_x = hal::adc::AdcPin::new(pins.gpio26).unwrap();
|
||||
let mut joy_y = hal::adc::AdcPin::new(pins.gpio27).unwrap();
|
||||
let pwm_slices = hal::pwm::Slices::new(pac.PWM, &mut pac.RESETS);
|
||||
let mut spk = pwm_slices.pwm6; // GP13 = PWM slice 6, channel B
|
||||
spk.set_div_int(125);
|
||||
spk.set_top(600); // ~2 kHz click
|
||||
spk.enable();
|
||||
spk.channel_b.output_to(pins.gpio13);
|
||||
|
||||
// ---- built-in grooves ----
|
||||
const GROOVES: [&str; 4] = [
|
||||
"t120;kick:4=X.x.;snare:4=.X.X;hatClosed:4/2=xxxxxxxx",
|
||||
"t92;kick:4/3=X....x...x..;snare:4/3=..gg.gX.gg.g;hatClosed:4/3=X.xX.xX.xX.x",
|
||||
"t140;kick:4=X..x;snare:4=.X.X;hatClosed:4/4=xxxxxxxxxxxxxxxx",
|
||||
"t100;kick:4=Xxxx;claves:5=Xxxxx~",
|
||||
];
|
||||
const NAMES: [&str; 4] = ["Four on the floor", "Half-time shuffle", "Driving 16ths", "5 over 4"];
|
||||
|
||||
let mut idx = 0usize;
|
||||
let mut track = track_format::parse(GROOVES[idx]);
|
||||
let mut tempo: i64 = track.bpm;
|
||||
let mut playing = true;
|
||||
let mut notation = false;
|
||||
|
||||
let mut bar_start = timer.get_counter().ticks();
|
||||
let mut last_step = usize::MAX;
|
||||
let mut click_off_us: u64 = 0;
|
||||
let (mut pa, mut pb) = (false, false);
|
||||
let mut joy_zone = 0i8;
|
||||
let mut full_redraw = true;
|
||||
let mut last_draw_us = 0u64;
|
||||
// ---- MINIMAL ISOLATION ----
|
||||
// Heap is initialised above. Exercise the allocator once (parse), then just draw the
|
||||
// confirmed-working pattern in a loop. No inputs/audio/clock. If the screen shows blue +
|
||||
// corners → heap + parse + display are all fine and the bug is in the metronome loop logic.
|
||||
// If still black → the heap/parse path breaks the display.
|
||||
let _t = track_format::parse("t120;kick:4=Xxxx;snare:4=.X.X");
|
||||
let mut hb = false;
|
||||
let mut hb_us = 0u64;
|
||||
led.set_low().unwrap();
|
||||
|
||||
loop {
|
||||
pm_ui::draw_ui(&mut display).ok();
|
||||
let now = timer.get_counter().ticks();
|
||||
|
||||
// ---- inputs ----
|
||||
let a = btn_a.is_low().unwrap_or(false);
|
||||
let b = btn_b.is_low().unwrap_or(false);
|
||||
if a && !pa {
|
||||
playing = !playing;
|
||||
if playing {
|
||||
bar_start = now;
|
||||
last_step = usize::MAX;
|
||||
}
|
||||
full_redraw = true;
|
||||
}
|
||||
if b && !pb {
|
||||
notation = !notation;
|
||||
full_redraw = true;
|
||||
}
|
||||
pa = a;
|
||||
pb = b;
|
||||
|
||||
let jx = adc.read(&mut joy_x).unwrap_or(2048);
|
||||
let jy = adc.read(&mut joy_y).unwrap_or(2048);
|
||||
// rotate the joystick reading 90° CCW for control
|
||||
let rx = jy as i32;
|
||||
let ry = 4095 - jx as i32;
|
||||
let zone: i8 = if ry > 3200 { 1 } else if ry < 900 { 2 } else if rx > 3200 { 3 } else if rx < 900 { 4 } else { 0 };
|
||||
if zone != 0 && joy_zone == 0 {
|
||||
match zone {
|
||||
1 => tempo = (tempo + 4).min(300), // up → tempo+
|
||||
2 => tempo = (tempo - 4).max(30), // down → tempo-
|
||||
3 => {
|
||||
idx = (idx + 1) % GROOVES.len(); // right → next groove
|
||||
track = track_format::parse(GROOVES[idx]);
|
||||
tempo = track.bpm;
|
||||
bar_start = now;
|
||||
last_step = usize::MAX;
|
||||
}
|
||||
4 => {
|
||||
idx = (idx + GROOVES.len() - 1) % GROOVES.len(); // left → prev
|
||||
track = track_format::parse(GROOVES[idx]);
|
||||
tempo = track.bpm;
|
||||
bar_start = now;
|
||||
last_step = usize::MAX;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
full_redraw = true;
|
||||
}
|
||||
joy_zone = zone;
|
||||
|
||||
// ---- clock ----
|
||||
let master = &track.lanes[0];
|
||||
let msteps = master.levels.len().max(1) as u64;
|
||||
let beats = master.groups.iter().map(|&g| g as u64).sum::<u64>().max(1);
|
||||
let bar_us = 60_000_000u64 * beats / tempo.max(1) as u64;
|
||||
let step_us = (bar_us / msteps).max(1);
|
||||
let elapsed = now.wrapping_sub(bar_start) % bar_us;
|
||||
let cur_step = (elapsed / step_us) as usize;
|
||||
let phase = elapsed as f32 / bar_us as f32;
|
||||
|
||||
// ---- audio: click on a new step that has a hit ----
|
||||
if playing && cur_step != last_step {
|
||||
let lvl = master.levels[cur_step.min(msteps as usize - 1)];
|
||||
if lvl > 0 {
|
||||
let _ = spk.channel_b.set_duty_cycle(if lvl == 2 { 380 } else { 240 });
|
||||
click_off_us = now + if lvl == 2 { 28_000 } else { 14_000 };
|
||||
}
|
||||
last_step = cur_step;
|
||||
}
|
||||
if now >= click_off_us {
|
||||
let _ = spk.channel_b.set_duty_cycle(0);
|
||||
}
|
||||
|
||||
// ---- draw: on change, and periodically (so a draw lost right after init reappears, and
|
||||
// the playhead advances). ~7 fps; partial/playhead-only redraw is the next optimization. ----
|
||||
if full_redraw || now.wrapping_sub(last_draw_us) > 140_000 {
|
||||
// DIAGNOSTIC: draw the confirmed-working bring-up pattern instead of the metronome,
|
||||
// to isolate whether the blank screen is draw_metronome or the heap/display path.
|
||||
// If you see blue + 4 colored corners + "TL"/"PMK" → display+heap are fine and the
|
||||
// bug is in draw_metronome. If still black → it's the heap/display, not the drawing.
|
||||
let _ = (notation, idx, tempo, phase, &track); // keep state alive while diagnosing
|
||||
pm_ui::draw_ui(&mut display).ok();
|
||||
full_redraw = false;
|
||||
last_draw_us = now;
|
||||
}
|
||||
|
||||
// heartbeat LED (~1 Hz)
|
||||
if now.wrapping_sub(hb_us) > 500_000 {
|
||||
hb = !hb;
|
||||
let _ = if hb { led.set_high() } else { led.set_low() };
|
||||
hb_us = now;
|
||||
}
|
||||
timer.delay_ms(8);
|
||||
timer.delay_ms(50);
|
||||
}
|
||||
}
|
||||
|
||||
/// picotool metadata (visible via `picotool info`).
|
||||
#[link_section = ".bi_entries"]
|
||||
#[used]
|
||||
pub static PICOTOOL_ENTRIES: [hal::binary_info::EntryAddr; 1] =
|
||||
[hal::binary_info::rp_program_name!(c"pm-kit display")];
|
||||
|
|
|
|||
Loading…
Reference in a new issue