From bdf3e9f463518b38366427f246a734b126c30c0d Mon Sep 17 00:00:00 2001 From: Daniel Schaefer Date: Tue, 27 Jun 2023 14:49:37 +0800 Subject: [PATCH] ledmatrix: Limit max brightness, and do custom brightness control The new hardware is extremely bright, so we need to limit the brightness. We limit it so that the max power consumption is slightly under 500mA. This limit is done by using the scaling factor in the LED controller. This also has the advantage that we have more brightness levels available for each LED because we can use the full range (previously much of the range would be too bright to use, even with the first hardware revision). But since we can't use the scaling factor for brightness control anymore, we have to do the brightness calculation ourselves. Signed-off-by: Daniel Schaefer --- fl16-inputmodules/src/control.rs | 10 +--- fl16-inputmodules/src/fl16.rs | 1 + fl16-inputmodules/src/matrix.rs | 2 +- fl16-inputmodules/src/patterns.rs | 14 ++++- ledmatrix/src/main.rs | 90 +++++++++++++++++-------------- 5 files changed, 67 insertions(+), 50 deletions(-) diff --git a/fl16-inputmodules/src/control.rs b/fl16-inputmodules/src/control.rs index f5042f0..ec28c54 100644 --- a/fl16-inputmodules/src/control.rs +++ b/fl16-inputmodules/src/control.rs @@ -479,10 +479,7 @@ pub fn handle_command( } Command::SetBrightness(br) => { //let _ = serial.write("Brightness".as_bytes()); - state.brightness = *br; - matrix - .set_scaling(state.brightness) - .expect("failed to set scaling"); + set_brightness(state, *br, matrix); None } Command::Percentage(p) => { @@ -499,10 +496,7 @@ pub fn handle_command( PatternVals::ZigZag => state.grid = zigzag(), PatternVals::FullBrightness => { state.grid = percentage(100); - state.brightness = 0xFF; - matrix - .set_scaling(state.brightness) - .expect("failed to set scaling"); + set_brightness(state, BRIGHTNESS_LEVELS, matrix); } PatternVals::DisplayPanic => state.grid = display_panic(), PatternVals::DisplayLotus2 => state.grid = display_lotus2(), diff --git a/fl16-inputmodules/src/fl16.rs b/fl16-inputmodules/src/fl16.rs index 05e1f44..7bf3efc 100644 --- a/fl16-inputmodules/src/fl16.rs +++ b/fl16-inputmodules/src/fl16.rs @@ -20,6 +20,7 @@ where self.device.i2c } + // TODO: Maybe make this private and set it once in the constructor pub fn set_scaling(&mut self, scale: u8) -> Result<(), I2cError> { self.device.set_scaling(scale) } diff --git a/fl16-inputmodules/src/matrix.rs b/fl16-inputmodules/src/matrix.rs index 63d5612..02f3529 100644 --- a/fl16-inputmodules/src/matrix.rs +++ b/fl16-inputmodules/src/matrix.rs @@ -28,7 +28,7 @@ pub struct LedmatrixState { #[derive(Clone)] pub enum SleepState { Awake, - Sleeping(Grid), + Sleeping((Grid, u8)), } #[allow(clippy::large_enum_variant)] diff --git a/fl16-inputmodules/src/patterns.rs b/fl16-inputmodules/src/patterns.rs index 10d093f..5f46b55 100644 --- a/fl16-inputmodules/src/patterns.rs +++ b/fl16-inputmodules/src/patterns.rs @@ -12,6 +12,9 @@ use crate::matrix::*; /// math.ceil(WIDTH * HEIGHT / 8) pub const DRAW_BYTES: usize = 39; +/// Maximum number of brightneses levels +pub const BRIGHTNESS_LEVELS: u8 = 255; + pub type Foo = LedMatrix< bsp::hal::I2C< I2C1, @@ -270,14 +273,21 @@ pub fn _fill_grid(grid: &Grid, matrix: &mut Foo) { } } +pub fn set_brightness(state: &mut LedmatrixState, brightness: u8, matrix: &mut Foo) { + state.brightness = brightness; + fill_grid_pixels(state, matrix); +} + /// Just sends two I2C commands for the entire grid -pub fn fill_grid_pixels(grid: &Grid, matrix: &mut Foo) { +pub fn fill_grid_pixels(state: &LedmatrixState, matrix: &mut Foo) { // 0xB4 LEDs on the first page, 0xAB on the second page let mut brightnesses = [0x00; 0xB4 + 0xAB]; for y in 0..HEIGHT { for x in 0..WIDTH { let (register, page) = (matrix.device.calc_pixel)(x as u8, y as u8); - brightnesses[(page as usize) * 0xB4 + (register as usize)] = grid.0[x][y]; + brightnesses[(page as usize) * 0xB4 + (register as usize)] = + ((state.grid.0[x][y] as u64) * (state.brightness as u64) + / (BRIGHTNESS_LEVELS as u64)) as u8; } } matrix.device.fill_matrix(&brightnesses).unwrap(); diff --git a/ledmatrix/src/main.rs b/ledmatrix/src/main.rs index 15de5f1..ae5d9cc 100644 --- a/ledmatrix/src/main.rs +++ b/ledmatrix/src/main.rs @@ -16,6 +16,16 @@ use rp2040_hal::{ //use panic_probe as _; use rp2040_panic_usb_boot as _; +/// Static configuration whether sleep shohld instantly turn all LEDs on/off or +/// slowly fade themm on/off +const INSTANT_SLEEP: bool = false; + +const MAX_CURRENT: usize = 500; + +/// Maximum brightness out of 255 +/// Set to 94 because that results in just below 500mA current draw. +const MAX_BRIGHTNESS: u8 = 94; + // TODO: Doesn't work yet, unless I panic right at the beginning of main //#[cfg(not(debug_assertions))] //use core::panic::PanicInfo; @@ -65,9 +75,9 @@ use rp2040_panic_usb_boot as _; // .setup(&mut delay) // .expect("failed to setup rgb controller"); // -// matrix.set_scaling(100).expect("failed to set scaling"); +// set_brightness(state, 255, &mut matrix); // let grid = display_panic(); -// fill_grid_pixels(grid, &mut matrix); +// fill_grid_pixels(state, &mut matrix); // // loop {} //} @@ -166,7 +176,7 @@ fn main() -> ! { .manufacturer("Framework Computer Inc") .product("LED Matrix Input Module") .serial_number(serialnum) - .max_power(200) // Device uses roughly 164mW when all LEDs are at full brightness + .max_power(MAX_CURRENT) .device_release(device_release()) .device_class(USB_CLASS_CDC) .build(); @@ -193,22 +203,22 @@ fn main() -> ! { grid: percentage(0), col_buffer: Grid::default(), animate: false, - brightness: 120, + brightness: 51, // Default to 51/255 = 20% brightness sleeping: SleepState::Awake, game: None, - animation_period: 31_250, // 32 FPS + animation_period: 31_250, // 31,250 us = 32 FPS }; let mut matrix = LedMatrix::configure(i2c); matrix .setup(&mut delay) - .expect("failed to setup rgb controller"); + .expect("failed to setup RGB controller"); matrix - .set_scaling(state.brightness) + .set_scaling(MAX_BRIGHTNESS) .expect("failed to set scaling"); - fill_grid_pixels(&state.grid, &mut matrix); + fill_grid_pixels(&state, &mut matrix); let timer = Timer::new(pac.TIMER, &mut pac.RESETS); let mut prev_timer = timer.get_counter().ticks(); @@ -239,7 +249,7 @@ fn main() -> ! { _ => {} } - fill_grid_pixels(&state.grid, &mut matrix); + fill_grid_pixels(&state, &mut matrix); if state.animate { for x in 0..WIDTH { state.grid.0[x].rotate_right(1); @@ -297,7 +307,7 @@ fn main() -> ! { buf[0], buf[1], buf[2], buf[3] ) .unwrap(); - fill_grid_pixels(&state.grid, &mut matrix); + fill_grid_pixels(&state, &mut matrix); } _ => {} } @@ -366,21 +376,23 @@ fn handle_sleep( match (state.sleeping.clone(), go_sleeping) { (SleepState::Awake, false) => (), (SleepState::Awake, true) => { - state.sleeping = SleepState::Sleeping(state.grid.clone()); - //state.grid = display_sleep(); - fill_grid_pixels(&state.grid, matrix); + state.sleeping = SleepState::Sleeping((state.grid.clone(), state.brightness)); + // Perhaps we could have a sleep pattern. Probbaly not Or maybe + // just for the first couple of minutes? + // state.grid = display_sleep(); + // fill_grid_pixels(&state, matrix); // Slowly decrease brightness - delay.delay_ms(1000); - let mut brightness = state.brightness; - loop { - delay.delay_ms(100); - brightness = if brightness <= 5 { 0 } else { brightness - 5 }; - matrix - .set_scaling(brightness) - .expect("failed to set scaling"); - if brightness == 0 { - break; + if !INSTANT_SLEEP { + delay.delay_ms(1000); + let mut brightness = state.brightness; + loop { + delay.delay_ms(100); + brightness = if brightness <= 5 { 0 } else { brightness - 5 }; + set_brightness(state, brightness, matrix); + if brightness == 0 { + break; + } } } @@ -391,30 +403,30 @@ fn handle_sleep( //cortex_m::asm::wfi(); } (SleepState::Sleeping(_), true) => (), - (SleepState::Sleeping(old_grid), false) => { + (SleepState::Sleeping((old_grid, old_brightness)), false) => { // Restore back grid before sleeping state.sleeping = SleepState::Awake; state.grid = old_grid; - fill_grid_pixels(&state.grid, matrix); + fill_grid_pixels(state, matrix); // Power LED controller back on led_enable.set_high().unwrap(); // Slowly increase brightness - delay.delay_ms(1000); - let mut brightness = 0; - loop { - delay.delay_ms(100); - brightness = if brightness >= state.brightness - 5 { - state.brightness - } else { - brightness + 5 - }; - matrix - .set_scaling(brightness) - .expect("failed to set scaling"); - if brightness == state.brightness { - break; + if !INSTANT_SLEEP { + delay.delay_ms(1000); + let mut brightness = 0; + loop { + delay.delay_ms(100); + brightness = if brightness >= old_brightness - 5 { + old_brightness + } else { + brightness + 5 + }; + set_brightness(state, brightness, matrix); + if brightness == old_brightness { + break; + } } } }