Updated debounce and flash script
This commit is contained in:
parent
8d7ec43515
commit
9c64a137a7
@ -86,6 +86,7 @@ impl Board {
|
||||
hardware::MATRIX_DEBOUNCE_SCANS_PRESS,
|
||||
hardware::MATRIX_DEBOUNCE_SCANS_RELEASE,
|
||||
hardware::MIN_PRESS_SPACING_SCANS,
|
||||
hardware::RELEASE_GRACE_PERIOD_SCANS,
|
||||
);
|
||||
button_matrix.init_pins();
|
||||
|
||||
|
||||
@ -63,8 +63,10 @@ pub struct ButtonMatrix<P, const ROWS: usize, const COLS: usize, const KEYS: usi
|
||||
release_threshold: u8,
|
||||
debounce_counter: [u8; KEYS],
|
||||
last_press_scan: [u32; KEYS],
|
||||
last_release_scan: [u32; KEYS],
|
||||
scan_counter: u32,
|
||||
min_press_gap_scans: u32,
|
||||
release_grace_period_scans: u32,
|
||||
}
|
||||
|
||||
impl<P, const ROWS: usize, const COLS: usize, const KEYS: usize> ButtonMatrix<P, ROWS, COLS, KEYS>
|
||||
@ -76,6 +78,7 @@ where
|
||||
press_threshold: u8,
|
||||
release_threshold: u8,
|
||||
min_press_gap_scans: u32,
|
||||
release_grace_period_scans: u32,
|
||||
) -> Self {
|
||||
debug_assert_eq!(KEYS, ROWS * COLS);
|
||||
Self {
|
||||
@ -85,8 +88,10 @@ where
|
||||
release_threshold,
|
||||
debounce_counter: [0; KEYS],
|
||||
last_press_scan: [0; KEYS],
|
||||
last_release_scan: [0; KEYS],
|
||||
scan_counter: 0,
|
||||
min_press_gap_scans,
|
||||
release_grace_period_scans,
|
||||
}
|
||||
}
|
||||
|
||||
@ -133,7 +138,11 @@ where
|
||||
if self.debounce_counter[button_index] >= threshold {
|
||||
self.pressed[button_index] = match current_state {
|
||||
true => self.should_register_press(button_index),
|
||||
false => false,
|
||||
false => {
|
||||
// Track release events for grace period
|
||||
self.last_release_scan[button_index] = self.scan_counter;
|
||||
false
|
||||
}
|
||||
};
|
||||
self.debounce_counter[button_index] = 0;
|
||||
}
|
||||
@ -145,9 +154,13 @@ where
|
||||
}
|
||||
|
||||
fn should_register_press(&mut self, button_index: usize) -> bool {
|
||||
let elapsed = self.scan_counter.wrapping_sub(self.last_press_scan[button_index]);
|
||||
let can_register = self.last_press_scan[button_index] == 0
|
||||
|| elapsed >= self.min_press_gap_scans;
|
||||
let press_elapsed = self.scan_counter.wrapping_sub(self.last_press_scan[button_index]);
|
||||
let release_elapsed = self.scan_counter.wrapping_sub(self.last_release_scan[button_index]);
|
||||
|
||||
let can_register = (self.last_press_scan[button_index] == 0
|
||||
|| press_elapsed >= self.min_press_gap_scans)
|
||||
&& (self.last_release_scan[button_index] == 0
|
||||
|| release_elapsed >= self.release_grace_period_scans);
|
||||
|
||||
if can_register {
|
||||
self.last_press_scan[button_index] = self.scan_counter;
|
||||
@ -211,7 +224,7 @@ mod tests {
|
||||
let row_state = Rc::new(Cell::new(false));
|
||||
let column_state = Rc::new(Cell::new(false));
|
||||
let pins = MockMatrixPins::new(row_state.clone(), column_state.clone());
|
||||
let matrix = ButtonMatrix::new(pins, 5, 5, 200);
|
||||
let matrix = ButtonMatrix::new(pins, 5, 5, 200, 100);
|
||||
(matrix, row_state, column_state)
|
||||
}
|
||||
|
||||
|
||||
@ -7,13 +7,17 @@ use rp2040_hal::gpio::Pins;
|
||||
pub const XTAL_FREQ_HZ: u32 = 12_000_000;
|
||||
|
||||
/// Debounce scans required before a key state toggles.
|
||||
/// Increased from 2/3 to 5/5 to prevent double characters from key bounce.
|
||||
/// At 250μs scan rate: 5 scans = 1.25ms debounce time.
|
||||
pub const MATRIX_DEBOUNCE_SCANS_PRESS: u8 = 5;
|
||||
pub const MATRIX_DEBOUNCE_SCANS_RELEASE: u8 = 5;
|
||||
/// Increased from 5/5 to 8/8 to prevent double characters from key bounce.
|
||||
/// At 250μs scan rate: 8 scans = 2ms debounce time.
|
||||
pub const MATRIX_DEBOUNCE_SCANS_PRESS: u8 = 8;
|
||||
pub const MATRIX_DEBOUNCE_SCANS_RELEASE: u8 = 8;
|
||||
|
||||
/// Minimum scans between two press events for the same key (50ms at 250μs scan cadence).
|
||||
pub const MIN_PRESS_SPACING_SCANS: u32 = 200;
|
||||
/// Minimum scans between two press events for the same key (75ms at 250μs scan cadence).
|
||||
pub const MIN_PRESS_SPACING_SCANS: u32 = 300;
|
||||
|
||||
/// Grace period after key release before allowing new press (37.5ms at 250μs scan cadence).
|
||||
/// This prevents phantom presses from release bounce.
|
||||
pub const RELEASE_GRACE_PERIOD_SCANS: u32 = 150;
|
||||
|
||||
/// Initial scan iterations before trusting key state.
|
||||
pub const INITIAL_SCAN_PASSES: usize = 50;
|
||||
|
||||
@ -354,7 +354,7 @@ mod tests {
|
||||
#[test]
|
||||
fn idle_mode_backdates_start_time() {
|
||||
// Idle mode should start mid-breath so the LED resumes smoothly.
|
||||
let now = 10_000;
|
||||
let now: u32 = 10_000;
|
||||
let expected = now.saturating_sub(BREATH_PERIOD_MS / 2);
|
||||
assert_eq!(mode_start_time(StatusMode::Idle, now), expected);
|
||||
assert_eq!(mode_start_time(StatusMode::Active, now), now);
|
||||
|
||||
@ -41,9 +41,13 @@ def candidate_paths(explicit: str, user: str) -> list[Path]:
|
||||
if not root.exists() or not root.is_dir():
|
||||
continue
|
||||
# Many systems mount the UF2 volume directly as a child of the root directory.
|
||||
try:
|
||||
for child in root.iterdir():
|
||||
if child.is_dir():
|
||||
paths.append(child)
|
||||
except PermissionError:
|
||||
# Skip directories we can't read
|
||||
continue
|
||||
return paths
|
||||
|
||||
|
||||
@ -53,13 +57,11 @@ def choose_mount(explicit: str, user: str) -> Path | None:
|
||||
# For an explicit mount we only care whether it exists.
|
||||
path = Path(explicit)
|
||||
return path if path.exists() and path.is_dir() else None
|
||||
# Prefer candidates containing INFO_UF2.TXT, fall back to first existing directory.
|
||||
# Only accept candidates containing INFO_UF2.TXT (real RP2040 bootloader mounts)
|
||||
info_candidates = [path for path in candidates if (path / INFO_FILE).exists()]
|
||||
if info_candidates:
|
||||
return info_candidates[0]
|
||||
for path in candidates:
|
||||
if path.exists() and path.is_dir():
|
||||
return path
|
||||
# Don't fall back to random directories - only accept real RP2040 mounts
|
||||
return None
|
||||
|
||||
|
||||
@ -104,9 +106,13 @@ def main() -> int:
|
||||
)
|
||||
else:
|
||||
print(
|
||||
"Unable to detect RP2040 UF2 mount. Pass one via mount=/path",
|
||||
"Unable to detect RP2040 UF2 mount. Make sure device is in bootloader mode:",
|
||||
file=sys.stderr,
|
||||
)
|
||||
print("1. Press reset/boot button on rp2040 Zero board", file=sys.stderr)
|
||||
print("2. Press upper left corner key when connecting USB", file=sys.stderr)
|
||||
print("3. Press Fn+Fn+Shift+Shift+Ctrl chord", file=sys.stderr)
|
||||
print("Then try again or pass explicit mount via mount=/run/media/$USER/RPI-RP2", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user