Skip to content

ledmatrix: Better debug mode #75

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 5 commits into from
Sep 10, 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
31 changes: 20 additions & 11 deletions .github/workflows/firmware.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ jobs:
- run: cargo make --cwd b1display
- run: cargo make --cwd c1minimal
- run: cargo make --cwd ledmatrix build-release
- run: cargo make --cwd ledmatrix build-release-10k
- run: cargo make --cwd b1display build-release
- run: cargo make --cwd c1minimal build-release

Expand All @@ -44,17 +45,9 @@ jobs:
sudo apt-get install -y libudev-dev
cargo make --cwd b1display uf2
cargo make --cwd c1minimal uf2
cargo make --cwd ledmatrix build-release-10k-uf2
cargo make --cwd ledmatrix uf2

- name: Upload UF2 files
uses: actions/upload-artifact@v3
with:
name: inputmodule_fw_uf2
path: |
b1display.uf2
c1minimal.uf2
ledmatrix.uf2

- name: Convert to bin format
run: |
sudo apt-get update
Expand All @@ -63,14 +56,30 @@ jobs:
cargo make --cwd c1minimal bin
cargo make --cwd ledmatrix bin

- name: Upload bin files
- name: Upload ledmatrix files
uses: actions/upload-artifact@v3
with:
name: inputmodule_fw_bin
path: |
ledmatrix.bin
ledmatrix.uf2
ledmatrix_10k.uf2

- name: Upload b1display files
uses: actions/upload-artifact@v3
with:
name: inputmodule_fw_uf2
path: |
b1display.bin
b1display.uf2

- name: Upload c1minimal files
uses: actions/upload-artifact@v3
with:
name: inputmodule_fw_uf2
path: |
c1minimal.bin
ledmatrix.bin
c1minimal.uf2

linting:
name: Linting
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/traditional-cargo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ env:

jobs:
firmware:
name: Building
name: Build firmware
runs-on: [ubuntu-latest]
steps:
- uses: actions/checkout@v3
Expand All @@ -32,6 +32,7 @@ jobs:
- run: cargo install flip-link

- run: cargo build -p ledmatrix
- run: cargo build -p ledmatrix --features 10k
- run: cargo build -p b1display
- run: cargo build -p c1minimal

Expand Down
2 changes: 1 addition & 1 deletion fl16-inputmodules/src/matrix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ pub enum SleepState {
Sleeping((Grid, u8)),
}

#[derive(Copy, Clone, Debug)]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum SleepReason {
Command,
SleepPin,
Expand Down
14 changes: 14 additions & 0 deletions fl16-inputmodules/src/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,3 +359,17 @@ pub fn zigzag() -> Grid {

grid
}

pub fn every_nth_col(n: usize) -> Grid {
let mut grid = Grid::default();

for y in 0..HEIGHT {
for x in 0..WIDTH {
if x % n == 0 {
grid.0[x][y] = 0xFF;
}
}
}

grid
}
3 changes: 3 additions & 0 deletions ledmatrix/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ edition = "2021"
name = "ledmatrix"
version = "0.1.6"

[features]
10k = []

[dependencies]
cortex-m.workspace = true
cortex-m-rt.workspace = true
Expand Down
10 changes: 10 additions & 0 deletions ledmatrix/Makefile.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
extend = "../Makefile.toml"

[tasks.build-release-10k]
command = "cargo"
args = ["build", "--target=thumbv6m-none-eabi", "--release", "--features", "10k"]

[tasks.build-release-10k-uf2]
command = "elf2uf2-rs"
args = ["../target/thumbv6m-none-eabi/release/ledmatrix", "../target/thumbv6m-none-eabi/release/ledmatrix_10k.uf2"]
dependencies = ["build-release-10k"]
install_crate = "elf2uf2-rs"

[tasks.uf2]
command = "elf2uf2-rs"
args = ["../target/thumbv6m-none-eabi/release/ledmatrix", "../target/thumbv6m-none-eabi/release/ledmatrix.uf2"]
Expand Down
132 changes: 95 additions & 37 deletions ledmatrix/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const MAX_CURRENT: usize = 500;
/// Maximum brightness out of 255
/// On HW Rev 1 from BizLink set to 94 to have just below 500mA current draw.
/// BizLink HW Rev 2 has a larger current limiting resistor
#[cfg(feature = "10k")]
const MAX_BRIGHTNESS: u8 = 94;
#[cfg(not(feature = "10k"))]
const MAX_BRIGHTNESS: u8 = 255;

// TODO: Doesn't work yet, unless I panic right at the beginning of main
Expand Down Expand Up @@ -215,6 +218,18 @@ fn main() -> ! {
&clocks.peripheral_clock,
);

// Detect whether the dip1 pin is connected
// We switched from bootsel button to DIP-switch with general purpose DIP1 pin in DVT2
let mut dip1_present = false;
let dip1 = pins.dip1.into_pull_up_input();
if dip1.is_low().unwrap() {
dip1_present = true;
}
let dip1 = dip1.into_pull_down_input();
if dip1.is_high().unwrap() {
dip1_present = true;
}

let mut state = LedmatrixState {
grid: percentage(0),
col_buffer: Grid::default(),
Expand All @@ -226,6 +241,14 @@ fn main() -> ! {
pwm_freq: PwmFreqArg::P29k,
debug_mode: false,
};
if dip1_present {
state.debug_mode = dip1.is_high().unwrap();
}
if !show_startup_animation(&state) {
// If no startup animation, render another pattern
// Lighting up every second column is a good pattern to test for noise.
state.grid = every_nth_col(2);
};

let mut matrix = LedMatrix::configure(i2c);
matrix
Expand All @@ -246,9 +269,6 @@ fn main() -> ! {
let mut sleep_timer = timer.get_counter().ticks();

let mut startup_percentage = Some(0);
if !STARTUP_ANIMATION || state.debug_mode {
state.grid = percentage(100);
}

// Detect whether the sleep pin is connected
// Early revisions of the hardware didn't have it wired up, if that is the
Expand All @@ -263,25 +283,16 @@ fn main() -> ! {
sleep_present = true;
}

// Detect whether the dip1 pin is connected
// We switched from bootsel button to DIP-switch with general purpose DIP1 pin in DVT2
let mut dip1_present = false;
let dip1 = pins.dip1.into_pull_up_input();
if dip1.is_low().unwrap() {
dip1_present = true;
}
let dip1 = dip1.into_pull_down_input();
if sleep.is_high().unwrap() {
dip1_present = true;
}

let mut usb_initialized = false;
let mut usb_suspended = false;
let mut last_usb_suspended = usb_suspended;
let mut sleep_reason: Option<SleepReason> = None;
let mut last_sleep_reason: Option<SleepReason>;
let mut last_host_sleep = sleep.is_low().unwrap();

loop {
last_sleep_reason = sleep_reason;

if dip1_present {
state.debug_mode = dip1.is_high().unwrap();
}
Expand All @@ -293,11 +304,13 @@ fn main() -> ! {
// Or if it currently sleeping. Don't change if not sleeping
// because then sleep is controlled by timing or by API.
if host_sleep_changed || host_sleeping {
sleep_reason = if host_sleeping {
Some(SleepReason::SleepPin)
} else {
None
};
sleep_reason = assign_sleep_reason(
last_sleep_reason,
sleep_reason,
host_sleeping,
host_sleep_changed,
SleepReason::SleepPin,
);
}
last_host_sleep = host_sleeping;
}
Expand All @@ -311,17 +324,25 @@ fn main() -> ! {
// initialized for the first time. But we don't want to show the
// sleep animation during startup.
if usb_initialized && (usb_suspended_changed || usb_suspended) {
sleep_reason = if usb_suspended {
Some(SleepReason::UsbSuspend)
} else {
None
};
sleep_reason = assign_sleep_reason(
last_sleep_reason,
sleep_reason,
usb_suspended,
usb_suspended_changed,
SleepReason::UsbSuspend,
);
}
last_usb_suspended = usb_suspended;

// Go to sleep after the timer has run out
if timer.get_counter().ticks() > sleep_timer + SLEEP_TIMEOUT && !state.debug_mode {
sleep_reason = Some(SleepReason::Timeout);
sleep_reason = assign_sleep_reason(
last_sleep_reason,
sleep_reason,
true,
true,
SleepReason::Timeout,
);
}
// Constantly resetting timer during sleep is same as reset it once on waking up.
// This means the timer ends up counting the time spent awake.
Expand All @@ -342,7 +363,7 @@ fn main() -> ! {
if matches!(state.sleeping, SleepState::Awake) && render_again {
// On startup slowly turn the screen on - it's a pretty effect :)
match startup_percentage {
Some(p) if p <= 100 && (STARTUP_ANIMATION || state.debug_mode) => {
Some(p) if p <= 100 && show_startup_animation(&state) => {
state.grid = percentage(p);
startup_percentage = Some(p + 5);
}
Expand Down Expand Up @@ -396,11 +417,13 @@ fn main() -> ! {
}
(Some(command), _) => {
if let Command::Sleep(go_sleeping) = command {
sleep_reason = if go_sleeping {
Some(SleepReason::Command)
} else {
None
};
sleep_reason = assign_sleep_reason(
last_sleep_reason,
sleep_reason,
go_sleeping,
true,
SleepReason::Command,
);
} else {
// If already sleeping, wake up.
// This means every command will wake the device up.
Expand Down Expand Up @@ -520,14 +543,40 @@ fn get_random_byte(rosc: &RingOscillator<Enabled>) -> u8 {
byte
}

fn dyn_sleep_mode(state: &mut LedmatrixState) -> SleepMode {
fn dyn_sleep_mode(state: &LedmatrixState) -> SleepMode {
if state.debug_mode {
SleepMode::Debug
} else {
SLEEP_MODE
}
}

fn debug_mode(state: &LedmatrixState) -> bool {
dyn_sleep_mode(state) == SleepMode::Debug
}

fn show_startup_animation(state: &LedmatrixState) -> bool {
// Show startup animation
STARTUP_ANIMATION && !debug_mode(state)
}

fn assign_sleep_reason(
previous: Option<SleepReason>,
current: Option<SleepReason>,
need_sleep: bool,
// Whether the signal has actually changed in between firing
signal_changed: bool,
new: SleepReason,
) -> Option<SleepReason> {
if !need_sleep {
None
} else if current.is_some() && (Some(new) == previous || !signal_changed) {
current
} else {
Some(new)
}
}

// Will do nothing if already in the right state
fn handle_sleep(
sleep_reason: Option<SleepReason>,
Expand All @@ -537,6 +586,7 @@ fn handle_sleep(
led_enable: &mut gpio::Pin<Gpio29, gpio::Output<gpio::PushPull>>,
) {
match (state.sleeping.clone(), sleep_reason) {
// Awake and staying awake
(SleepState::Awake, None) => (),
(SleepState::Awake, Some(sleep_reason)) => {
state.sleeping = SleepState::Sleeping((state.grid.clone(), state.brightness));
Expand All @@ -553,26 +603,34 @@ fn handle_sleep(
}
}

// Turn LED controller off to save power
if dyn_sleep_mode(state) == SleepMode::Debug {
if debug_mode(state) {
state.grid = display_sleep_reason(sleep_reason);
fill_grid_pixels(state, matrix);
} else {
// Turn LED controller off to save power
led_enable.set_low().unwrap();
}

// TODO: Set up SLEEP# pin as interrupt and wfi
//cortex_m::asm::wfi();
}
(SleepState::Sleeping(_), Some(_)) => (),
// Already sleeping and new sleep reason => just keep sleeping
(SleepState::Sleeping(_), Some(sleep_reason)) => {
// If debug mode is enabled, then make sure the latest sleep reason is displayed
if debug_mode(state) {
state.grid = display_sleep_reason(sleep_reason);
fill_grid_pixels(state, matrix);
}
}
// Sleeping and need to wake up
(SleepState::Sleeping((old_grid, old_brightness)), None) => {
// Restore back grid before sleeping
state.sleeping = SleepState::Awake;
state.grid = old_grid;
fill_grid_pixels(state, matrix);

// Power LED controller back on
if dyn_sleep_mode(state) != SleepMode::Debug {
if !debug_mode(state) {
led_enable.set_high().unwrap();
}

Expand Down
Loading