Skip to content

ledmatrix: Add debug mode and use DIP1 pin #69

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions fl16-inputmodules/src/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ pub enum CommandVals {
SetPowerMode = 0x1B,
AnimationPeriod = 0x1C,
PwmFreq = 0x1E,
DebugMode = 0x1F,
Version = 0x20,
}

Expand Down Expand Up @@ -205,6 +206,8 @@ pub enum Command {
#[cfg(feature = "ledmatrix")]
SetPwmFreq(PwmFreqArg),
GetPwmFreq,
SetDebugMode(bool),
GetDebugMode,
_Unknown,
}

Expand Down Expand Up @@ -385,6 +388,11 @@ pub fn parse_module_command(count: usize, buf: &[u8]) -> Option<Command> {
Some(Command::GetPwmFreq)
}
}
Some(CommandVals::DebugMode) => Some(if let Some(debug_mode) = arg {
Command::SetDebugMode(debug_mode == 1)
} else {
Command::GetDebugMode
}),
_ => None,
}
} else {
Expand Down Expand Up @@ -614,6 +622,15 @@ pub fn handle_command(
response[0] = state.pwm_freq as u8;
Some(response)
}
Command::SetDebugMode(arg) => {
state.debug_mode = *arg;
None
}
Command::GetDebugMode => {
let mut response: [u8; 32] = [0; 32];
response[0] = state.debug_mode as u8;
Some(response)
}
_ => handle_generic_command(command),
}
}
Expand Down
2 changes: 2 additions & 0 deletions fl16-inputmodules/src/led_hal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ pub use hal::pac;
hal::bsp_pins!(
/// GPIO 0 is connected to the SLEEP# pin of the EC
Gpio0 { name: sleep },
/// GPIO 25 is connected to the DIP Switch #1
Gpio25 { name: dip1 },
/// GPIO 26 is connected to I2C SDA of the LED controller
Gpio26 {
name: gpio26,
Expand Down
10 changes: 10 additions & 0 deletions fl16-inputmodules/src/mapping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,13 @@ pub const EXCLAMATION_MARK: SingleDisplayData = [
0b00000000,
0b00010000,
];
pub const HASH: SingleDisplayData = [
0b00100100,
0b00100111,
0b00111100,
0b11100100,
0b00100111,
0b00111100,
0b11100100,
0b00100100,
];
24 changes: 24 additions & 0 deletions fl16-inputmodules/src/matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,49 @@ impl Default for Grid {
}

pub struct LedmatrixState {
/// Currently displayed grid
pub grid: Grid,
/// Temporary buffer for building a new grid
pub col_buffer: Grid,
/// Whether the grid is currently being animated
pub animate: bool,
/// LED brightness out of 255
pub brightness: u8,
/// Current sleep state
pub sleeping: SleepState,
/// State of the current game, if any
pub game: Option<GameState>,
pub animation_period: u64,
/// Current LED PWM frequency
pub pwm_freq: PwmFreqArg,
/// Whether debug mode is active
///
/// In debug mode:
/// - Startup is instant, no animation
/// - Sleep/wake transition is instant, no animation/fading
/// - No automatic sleeping
pub debug_mode: bool,
}

#[allow(clippy::large_enum_variant)]
#[derive(Clone)]
/// Whether asleep or not, if asleep contains data to restore previous LED grid
pub enum SleepState {
Awake,
Sleeping((Grid, u8)),
}

#[derive(Copy, Clone, Debug)]
pub enum SleepReason {
Command,
SleepPin,
Timeout,
UsbSuspend,
}

#[allow(clippy::large_enum_variant)]
#[derive(Clone)]
/// State that's used for each game
pub enum GameState {
Snake(SnakeState),
Pong(PongState),
Expand Down
31 changes: 31 additions & 0 deletions fl16-inputmodules/src/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,37 @@ pub fn draw_grey_col(grid: &mut Grid, col: u8, levels: &[u8; HEIGHT]) {
grid.0[8 - col as usize][..HEIGHT].copy_from_slice(&levels[..HEIGHT]);
}

pub fn display_sleep_reason(sleep_reason: SleepReason) -> Grid {
let mut grid = Grid::default();

match sleep_reason {
SleepReason::Command => {
display_letter(20, &mut grid, CAP_C);
display_letter(10, &mut grid, CAP_M);
display_letter(0, &mut grid, CAP_D);
}
SleepReason::SleepPin => {
display_letter(23, &mut grid, CAP_S);
display_letter(13, &mut grid, CAP_L);
display_letter(7, &mut grid, CAP_P);
display_letter(0, &mut grid, HASH);
}
SleepReason::Timeout => {
display_letter(24, &mut grid, CAP_T);
display_letter(16, &mut grid, CAP_I);
display_letter(8, &mut grid, CAP_M);
display_letter(0, &mut grid, CAP_E);
}
SleepReason::UsbSuspend => {
display_letter(17, &mut grid, CAP_U);
display_letter(10, &mut grid, CAP_S);
display_letter(0, &mut grid, CAP_B);
}
};

grid
}

pub fn display_sleep() -> Grid {
Grid([
[
Expand Down
24 changes: 24 additions & 0 deletions inputmodule-control/src/inputmodule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum Command {
PowerMode = 0x1B,
AnimationPeriod = 0x1C,
PwmFreq = 0x1E,
DebugMode = 0x1F,
Version = 0x20,
}

Expand Down Expand Up @@ -225,6 +226,9 @@ pub fn serial_commands(args: &crate::ClapCli) {
if let Some(freq) = ledmatrix_args.pwm_freq {
pwm_freq_cmd(serialdev, freq);
}
if let Some(debug_mode) = ledmatrix_args.debug_mode {
debug_mode_cmd(serialdev, debug_mode);
}

if ledmatrix_args.stop_game {
simple_cmd(
Expand Down Expand Up @@ -446,6 +450,26 @@ fn sleeping_cmd(serialdev: &str, arg: Option<bool>) {
}
}

fn debug_mode_cmd(serialdev: &str, arg: Option<bool>) {
let mut port = serialport::new(serialdev, 115_200)
.timeout(SERIAL_TIMEOUT)
.open()
.expect("Failed to open port");

if let Some(enable_debug) = arg {
simple_cmd_port(&mut port, Command::DebugMode, &[u8::from(enable_debug)]);
} else {
simple_cmd_port(&mut port, Command::DebugMode, &[]);

let mut response: Vec<u8> = vec![0; 32];
port.read_exact(response.as_mut_slice())
.expect("Found no data!");

let debug_mode: bool = response[0] == 1;
println!("Debug Mode enabled: {debug_mode}");
}
}

fn brightness_cmd(serialdev: &str, arg: Option<u8>) {
let mut port = serialport::new(serialdev, 115_200)
.timeout(SERIAL_TIMEOUT)
Expand Down
4 changes: 4 additions & 0 deletions inputmodule-control/src/ledmatrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ pub struct LedMatrixSubcommand {
#[clap(value_enum)]
pub pwm_freq: Option<Option<u16>>,

/// Set debug mode or get current mode, if no value provided
#[arg(long)]
pub debug_mode: Option<Option<bool>>,

/// Crash the firmware (TESTING ONLY!)
#[arg(long)]
pub panic: bool,
Expand Down
28 changes: 28 additions & 0 deletions ledmatrix/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,31 @@ Any other command will also wake up the device.

The idle timer will send the device to sleep after a configured timeout (default 60 seconds).
The idle timer is reset once the device wakes up or once it receives a command.

## DIP Switch

LED Matrix hardware since DVT2 (September 2023) has a DIP switch with two
switches, let's call them DIP1 and DIP2.

###### DIP2 (Bootloader)

DIP2 is the bootloader switch. To enter bootloader mode follow these steps:

1. Unplug module and flip the switch to ON
2. Plug module back in, it will appear as a flash drive with the name `RPI-RP2`
3. Copy the firmware `.uf2` file onto that drive, it will automatically flash and reappear as a flash drive
4. To exit bootloader mode, unplug the module to flip the switch back, and plug it back in
5. Now the new firmware should be running

###### DIP1 (General Purpose)

DIP1 could serve many purposes. Currently it is configured to enable the debug mode.
When debug mode is enabled and the module goes to sleep, it will not turn the LEDs off to save power.
Instead it will display the reason why it went to sleep. This is useful for debugging module and host system behavior.

Sleep Reasons can be:

- `SLEEP#` pin: `SLP#`
- USB Suspend: `USB`
- Command: `CMD`
- Idle timer: `TIME`
Loading