Skip to content

Commit e10c4f1

Browse files
committed
ledmatrix: Add debug mode and use DIP1 pin
DIP1 pin enables debug mode, which displays why the module went to sleep. Great for debugging. Signed-off-by: Daniel Schaefer <dhs@frame.work>
1 parent 2ca3fb7 commit e10c4f1

File tree

5 files changed

+126
-28
lines changed

5 files changed

+126
-28
lines changed

fl16-inputmodules/src/led_hal.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ pub use hal::pac;
1717
hal::bsp_pins!(
1818
/// GPIO 0 is connected to the SLEEP# pin of the EC
1919
Gpio0 { name: sleep },
20+
/// GPIO 25 is connected to the DIP Switch #1
21+
Gpio25 { name: dip1 },
2022
/// GPIO 26 is connected to I2C SDA of the LED controller
2123
Gpio26 {
2224
name: gpio26,

fl16-inputmodules/src/mapping.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,3 +328,13 @@ pub const EXCLAMATION_MARK: SingleDisplayData = [
328328
0b00000000,
329329
0b00010000,
330330
];
331+
pub const HASH: SingleDisplayData = [
332+
0b00100100,
333+
0b00100111,
334+
0b00111100,
335+
0b11100100,
336+
0b00100111,
337+
0b00111100,
338+
0b11100100,
339+
0b00100100,
340+
];

fl16-inputmodules/src/matrix.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,49 @@ impl Default for Grid {
1616
}
1717

1818
pub struct LedmatrixState {
19+
/// Currently displayed grid
1920
pub grid: Grid,
21+
/// Temporary buffer for building a new grid
2022
pub col_buffer: Grid,
23+
/// Whether the grid is currently being animated
2124
pub animate: bool,
25+
/// LED brightness out of 255
2226
pub brightness: u8,
27+
/// Current sleep state
2328
pub sleeping: SleepState,
29+
/// State of the current game, if any
2430
pub game: Option<GameState>,
2531
pub animation_period: u64,
32+
/// Current LED PWM frequency
2633
pub pwm_freq: PwmFreqArg,
34+
/// Whether debug mode is active
35+
///
36+
/// In debug mode:
37+
/// - Startup is instant, no animation
38+
/// - Sleep/wake transition is instant, no animation/fading
39+
/// - No automatic sleeping
40+
pub debug_mode: bool,
2741
}
2842

2943
#[allow(clippy::large_enum_variant)]
3044
#[derive(Clone)]
45+
/// Whether asleep or not, if asleep contains data to restore previous LED grid
3146
pub enum SleepState {
3247
Awake,
3348
Sleeping((Grid, u8)),
3449
}
3550

51+
#[derive(Copy, Clone, Debug)]
52+
pub enum SleepReason {
53+
Command,
54+
SleepPin,
55+
Timeout,
56+
UsbSuspend,
57+
}
58+
3659
#[allow(clippy::large_enum_variant)]
3760
#[derive(Clone)]
61+
/// State that's used for each game
3862
pub enum GameState {
3963
Snake(SnakeState),
4064
Pong(PongState),

fl16-inputmodules/src/patterns.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,37 @@ pub fn draw_grey_col(grid: &mut Grid, col: u8, levels: &[u8; HEIGHT]) {
5050
grid.0[8 - col as usize][..HEIGHT].copy_from_slice(&levels[..HEIGHT]);
5151
}
5252

53+
pub fn display_sleep_reason(sleep_reason: SleepReason) -> Grid {
54+
let mut grid = Grid::default();
55+
56+
match sleep_reason {
57+
SleepReason::Command => {
58+
display_letter(20, &mut grid, CAP_C);
59+
display_letter(10, &mut grid, CAP_M);
60+
display_letter(0, &mut grid, CAP_D);
61+
}
62+
SleepReason::SleepPin => {
63+
display_letter(23, &mut grid, CAP_S);
64+
display_letter(13, &mut grid, CAP_L);
65+
display_letter(7, &mut grid, CAP_P);
66+
display_letter(0, &mut grid, HASH);
67+
}
68+
SleepReason::Timeout => {
69+
display_letter(24, &mut grid, CAP_T);
70+
display_letter(16, &mut grid, CAP_I);
71+
display_letter(8, &mut grid, CAP_M);
72+
display_letter(0, &mut grid, CAP_E);
73+
}
74+
SleepReason::UsbSuspend => {
75+
display_letter(17, &mut grid, CAP_U);
76+
display_letter(10, &mut grid, CAP_S);
77+
display_letter(0, &mut grid, CAP_B);
78+
}
79+
};
80+
81+
grid
82+
}
83+
5384
pub fn display_sleep() -> Grid {
5485
Grid([
5586
[

ledmatrix/src/main.rs

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ fn main() -> ! {
224224
game: None,
225225
animation_period: 31_250, // 31,250 us = 32 FPS
226226
pwm_freq: PwmFreqArg::P29k,
227+
debug_mode: false,
227228
};
228229

229230
let mut matrix = LedMatrix::configure(i2c);
@@ -245,7 +246,7 @@ fn main() -> ! {
245246
let mut sleep_timer = timer.get_counter().ticks();
246247

247248
let mut startup_percentage = Some(0);
248-
if !STARTUP_ANIMATION {
249+
if !STARTUP_ANIMATION || state.debug_mode {
249250
state.grid = percentage(100);
250251
}
251252

@@ -262,13 +263,28 @@ fn main() -> ! {
262263
sleep_present = true;
263264
}
264265

266+
// Detect whether the dip1 pin is connected
267+
// We switched from bootsel button to DIP-switch with general purpose DIP1 pin in DVT2
268+
let mut dip1_present = false;
269+
let dip1 = pins.dip1.into_pull_up_input();
270+
if dip1.is_low().unwrap() {
271+
dip1_present = true;
272+
}
273+
let dip1 = dip1.into_pull_down_input();
274+
if sleep.is_high().unwrap() {
275+
dip1_present = true;
276+
}
277+
265278
let mut usb_initialized = false;
266279
let mut usb_suspended = false;
267280
let mut last_usb_suspended = usb_suspended;
268-
let mut sleeping = false;
281+
let mut sleep_reason: Option<SleepReason> = None;
269282
let mut last_host_sleep = sleep.is_low().unwrap();
270283

271284
loop {
285+
if dip1_present {
286+
state.debug_mode = dip1.is_high().unwrap();
287+
}
272288
if sleep_present {
273289
// Go to sleep if the host is sleeping
274290
let host_sleeping = sleep.is_low().unwrap();
@@ -277,7 +293,11 @@ fn main() -> ! {
277293
// Or if it currently sleeping. Don't change if not sleeping
278294
// because then sleep is controlled by timing or by API.
279295
if host_sleep_changed || host_sleeping {
280-
sleeping = host_sleeping;
296+
sleep_reason = if host_sleeping {
297+
Some(SleepReason::SleepPin)
298+
} else {
299+
None
300+
};
281301
}
282302
last_host_sleep = host_sleeping;
283303
}
@@ -291,22 +311,26 @@ fn main() -> ! {
291311
// initialized for the first time. But we don't want to show the
292312
// sleep animation during startup.
293313
if usb_initialized && (usb_suspended_changed || usb_suspended) {
294-
sleeping = usb_suspended;
314+
sleep_reason = if usb_suspended {
315+
Some(SleepReason::UsbSuspend)
316+
} else {
317+
None
318+
};
295319
}
296320
last_usb_suspended = usb_suspended;
297321

298322
// Go to sleep after the timer has run out
299-
if timer.get_counter().ticks() > sleep_timer + SLEEP_TIMEOUT {
300-
sleeping = true;
323+
if timer.get_counter().ticks() > sleep_timer + SLEEP_TIMEOUT && !state.debug_mode {
324+
sleep_reason = Some(SleepReason::Timeout);
301325
}
302326
// Constantly resetting timer during sleep is same as reset it once on waking up.
303327
// This means the timer ends up counting the time spent awake.
304-
if sleeping {
328+
if sleep_reason.is_some() {
305329
sleep_timer = timer.get_counter().ticks();
306330
}
307331

308332
handle_sleep(
309-
sleeping,
333+
sleep_reason,
310334
&mut state,
311335
&mut matrix,
312336
&mut delay,
@@ -318,7 +342,7 @@ fn main() -> ! {
318342
if matches!(state.sleeping, SleepState::Awake) && render_again {
319343
// On startup slowly turn the screen on - it's a pretty effect :)
320344
match startup_percentage {
321-
Some(p) if p <= 100 && STARTUP_ANIMATION => {
345+
Some(p) if p <= 100 && (STARTUP_ANIMATION || state.debug_mode) => {
322346
state.grid = percentage(p);
323347
startup_percentage = Some(p + 5);
324348
}
@@ -372,12 +396,16 @@ fn main() -> ! {
372396
}
373397
(Some(command), _) => {
374398
if let Command::Sleep(go_sleeping) = command {
375-
sleeping = go_sleeping;
399+
sleep_reason = if go_sleeping {
400+
Some(SleepReason::Command)
401+
} else {
402+
None
403+
};
376404
} else {
377405
// If already sleeping, wake up.
378406
// This means every command will wake the device up.
379407
// Much more convenient than having to send the wakeup commmand.
380-
sleeping = false;
408+
sleep_reason = None;
381409
}
382410
// Make sure sleep animation only goes up to newly set brightness,
383411
// if setting the brightness causes wakeup
@@ -388,7 +416,7 @@ fn main() -> ! {
388416
}
389417
}
390418
handle_sleep(
391-
sleeping,
419+
sleep_reason,
392420
&mut state,
393421
&mut matrix,
394422
&mut delay,
@@ -492,25 +520,28 @@ fn get_random_byte(rosc: &RingOscillator<Enabled>) -> u8 {
492520
byte
493521
}
494522

523+
fn dyn_sleep_mode(state: &mut LedmatrixState) -> SleepMode {
524+
if state.debug_mode {
525+
SleepMode::Debug
526+
} else {
527+
SLEEP_MODE
528+
}
529+
}
530+
495531
// Will do nothing if already in the right state
496532
fn handle_sleep(
497-
go_sleeping: bool,
533+
sleep_reason: Option<SleepReason>,
498534
state: &mut LedmatrixState,
499535
matrix: &mut Foo,
500536
delay: &mut Delay,
501537
led_enable: &mut gpio::Pin<Gpio29, gpio::Output<gpio::PushPull>>,
502538
) {
503-
match (state.sleeping.clone(), go_sleeping) {
504-
(SleepState::Awake, false) => (),
505-
(SleepState::Awake, true) => {
539+
match (state.sleeping.clone(), sleep_reason) {
540+
(SleepState::Awake, None) => (),
541+
(SleepState::Awake, Some(sleep_reason)) => {
506542
state.sleeping = SleepState::Sleeping((state.grid.clone(), state.brightness));
507-
// Perhaps we could have a sleep pattern. Probbaly not Or maybe
508-
// just for the first couple of minutes?
509-
// state.grid = display_sleep();
510-
// fill_grid_pixels(&state, matrix);
511-
512543
// Slowly decrease brightness
513-
if SLEEP_MODE == SleepMode::Fading {
544+
if dyn_sleep_mode(state) == SleepMode::Fading {
514545
let mut brightness = state.brightness;
515546
loop {
516547
delay.delay_ms(100);
@@ -523,8 +554,8 @@ fn handle_sleep(
523554
}
524555

525556
// Turn LED controller off to save power
526-
if SLEEP_MODE == SleepMode::Debug {
527-
state.grid = display_sleep();
557+
if dyn_sleep_mode(state) == SleepMode::Debug {
558+
state.grid = display_sleep_reason(sleep_reason);
528559
fill_grid_pixels(state, matrix);
529560
} else {
530561
led_enable.set_low().unwrap();
@@ -533,20 +564,20 @@ fn handle_sleep(
533564
// TODO: Set up SLEEP# pin as interrupt and wfi
534565
//cortex_m::asm::wfi();
535566
}
536-
(SleepState::Sleeping(_), true) => (),
537-
(SleepState::Sleeping((old_grid, old_brightness)), false) => {
567+
(SleepState::Sleeping(_), Some(_)) => (),
568+
(SleepState::Sleeping((old_grid, old_brightness)), None) => {
538569
// Restore back grid before sleeping
539570
state.sleeping = SleepState::Awake;
540571
state.grid = old_grid;
541572
fill_grid_pixels(state, matrix);
542573

543574
// Power LED controller back on
544-
if SLEEP_MODE != SleepMode::Debug {
575+
if dyn_sleep_mode(state) != SleepMode::Debug {
545576
led_enable.set_high().unwrap();
546577
}
547578

548579
// Slowly increase brightness
549-
if SLEEP_MODE == SleepMode::Fading {
580+
if dyn_sleep_mode(state) == SleepMode::Fading {
550581
let mut brightness = 0;
551582
loop {
552583
delay.delay_ms(100);

0 commit comments

Comments
 (0)