Skip to content

Commit 865119a

Browse files
committed
b1display: Allow set/get of FPS and power mode
Signed-off-by: Daniel Schaefer <[email protected]>
1 parent 70529b5 commit 865119a

File tree

7 files changed

+314
-19
lines changed

7 files changed

+314
-19
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

b1display/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ Options:
6464
Invert screen on/off [possible values: true, false]
6565
--screen-saver [<SCREEN_SAVER>]
6666
Screensaver on/off [possible values: true, false]
67+
--fps [<FPS>]
68+
Set/get FPS [possible values: quarter, half, one, two, four, eight, sixteen, thirty-two]
69+
--power-mode [<POWER_MODE>]
70+
Set/get power mode [possible values: low, high]
6771
--image-bw <IMAGE_BW>
6872
Display black&white image (300x400px)
6973
--clear-ram

b1display/src/main.rs

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,18 @@ fn main() -> ! {
146146
&embedded_hal::spi::MODE_0,
147147
);
148148

149+
let mut state = B1DIsplayState {
150+
sleeping: SimpleSleepState::Awake,
151+
screen_inverted: false,
152+
screen_on: true,
153+
screensaver: Some(ScreenSaverState::default()),
154+
power_mode: PowerMode::Lpm,
155+
fps_config: FpsConfig {
156+
hpm: HpmFps::ThirtyTwo,
157+
lpm: LpmFps::Two,
158+
},
159+
};
160+
149161
const INVERTED: bool = false;
150162
const AUTO_PWRDOWN: bool = true;
151163
const TE_ENABLE: bool = true;
@@ -159,10 +171,7 @@ fn main() -> ! {
159171
INVERTED,
160172
AUTO_PWRDOWN,
161173
TE_ENABLE,
162-
FpsConfig {
163-
hpm: HpmFps::ThirtyTwo,
164-
lpm: LpmFps::One,
165-
},
174+
state.fps_config,
166175
WIDTH as u16,
167176
HEIGHT as u16,
168177
COL_START,
@@ -211,13 +220,6 @@ fn main() -> ! {
211220

212221
let sleep = pins.sleep.into_pull_down_input();
213222

214-
let mut state = B1DIsplayState {
215-
sleeping: SimpleSleepState::Awake,
216-
screen_inverted: false,
217-
screen_on: true,
218-
screensaver: Some(ScreenSaverState::default()),
219-
};
220-
221223
let timer = Timer::new(pac.TIMER, &mut pac.RESETS);
222224
let mut prev_timer = timer.get_counter().ticks();
223225

@@ -286,16 +288,16 @@ fn main() -> ! {
286288
(Some(c @ Command::BootloaderReset), _)
287289
| (Some(c @ Command::IsSleeping), _) => {
288290
if let Some(response) =
289-
handle_command(&c, &mut state, logo_rect, &mut disp)
291+
handle_command(&c, &mut state, logo_rect, &mut disp, &mut delay)
290292
{
291293
let _ = serial.write(&response);
292294
};
293295
}
294296
(Some(command), SimpleSleepState::Awake) => {
295297
// While sleeping no command is handled, except waking up
296-
if let Some(response) =
297-
handle_command(&command, &mut state, logo_rect, &mut disp)
298-
{
298+
if let Some(response) = handle_command(
299+
&command, &mut state, logo_rect, &mut disp, &mut delay,
300+
) {
299301
let _ = serial.write(&response);
300302
};
301303
// Must write AFTER writing response, otherwise the
@@ -352,7 +354,9 @@ fn handle_sleep<SPI, DC, CS, RST, const COLS: usize, const ROWS: usize>(
352354
//disp.on_off(true).unwrap();
353355
disp.sleep_out(delay).unwrap();
354356
// Sleep-in has to go into HPM first, so we'll be in HPM after wake-up as well
355-
disp.switch_mode(delay, PowerMode::Lpm).unwrap();
357+
if state.power_mode == PowerMode::Lpm {
358+
disp.switch_mode(delay, PowerMode::Lpm).unwrap();
359+
}
356360

357361
// Turn screensaver on when resuming from sleep
358362
// TODO Subject to change, but currently I want to avoid burn-in by default

control.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class CommandVals(IntEnum):
4141
FlushFramebuffer = 0x17
4242
ClearRam = 0x18
4343
ScreenSaver = 0x19
44+
SetFps = 0x1A
45+
SetPowerMode = 0x1B
4446
Version = 0x20
4547

4648

@@ -120,6 +122,9 @@ class GameControlVal(IntEnum):
120122

121123
RGB_COLORS = ['white', 'black', 'red', 'green',
122124
'blue', 'cyan', 'yellow', 'purple']
125+
SCREEN_FPS = ['quarter', 'half', 'one', 'two', 'four', 'eight', 'sixteen', 'thirtytwo']
126+
HIGH_FPS_MASK = 0b00010000
127+
LOW_FPS_MASK = 0b00000111
123128

124129
SERIAL_DEV = None
125130

@@ -196,6 +201,14 @@ def main():
196201
action=argparse.BooleanOptionalAction)
197202
parser.add_argument("--screen-saver", help="Turn on/off screensaver",
198203
action=argparse.BooleanOptionalAction)
204+
parser.add_argument("--set-fps", help="Set screen FPS",
205+
choices=SCREEN_FPS)
206+
parser.add_argument("--set-power-mode", help="Set screen power mode",
207+
choices=['high', 'low'])
208+
parser.add_argument("--get-fps", help="Set screen FPS",
209+
action='store_true')
210+
parser.add_argument("--get-power-mode", help="Set screen power mode",
211+
action='store_true')
199212
parser.add_argument("--b1image", help="On the B1 display, show a PNG or GIF image in black and white only)",
200213
type=argparse.FileType('rb'))
201214

@@ -282,6 +295,14 @@ def main():
282295
invert_screen_cmd(args.invert_screen)
283296
elif args.screen_saver is not None:
284297
screen_saver_cmd(args.screen_saver)
298+
elif args.set_fps is not None:
299+
set_fps_cmd(args.set_fps)
300+
elif args.set_power_mode is not None:
301+
set_power_mode_cmd(args.set_power_mode)
302+
elif args.get_fps:
303+
get_fps_cmd()
304+
elif args.get_power_mode:
305+
get_power_mode_cmd()
285306
elif args.b1image is not None:
286307
b1image_bl(args.b1image)
287308
elif args.version:
@@ -1073,6 +1094,92 @@ def screen_saver_cmd(on):
10731094
send_command(CommandVals.ScreenSaver, [on])
10741095

10751096

1097+
def set_fps_cmd(mode):
1098+
res = send_command(CommandVals.SetFps, with_response=True)
1099+
current_fps = res[0]
1100+
1101+
if mode == 'quarter':
1102+
fps = current_fps & ~LOW_FPS_MASK
1103+
fps |= 0b000
1104+
send_command(CommandVals.SetFps, [fps])
1105+
set_power_mode_cmd('low')
1106+
elif mode == 'half':
1107+
fps = current_fps & ~LOW_FPS_MASK
1108+
fps |= 0b001
1109+
send_command(CommandVals.SetFps, [fps])
1110+
set_power_mode_cmd('low')
1111+
elif mode == 'one':
1112+
fps = current_fps & ~LOW_FPS_MASK
1113+
fps |= 0b010
1114+
send_command(CommandVals.SetFps, [fps])
1115+
set_power_mode_cmd('low')
1116+
elif mode == 'two':
1117+
fps = current_fps & ~LOW_FPS_MASK
1118+
fps |= 0b011
1119+
send_command(CommandVals.SetFps, [fps])
1120+
set_power_mode_cmd('low')
1121+
elif mode == 'four':
1122+
fps = current_fps & ~LOW_FPS_MASK
1123+
fps |= 0b100
1124+
send_command(CommandVals.SetFps, [fps])
1125+
set_power_mode_cmd('low')
1126+
elif mode == 'eight':
1127+
fps = current_fps & ~LOW_FPS_MASK
1128+
fps |= 0b101
1129+
send_command(CommandVals.SetFps, [fps])
1130+
set_power_mode_cmd('low')
1131+
elif mode == 'sixteen':
1132+
fps = current_fps & ~HIGH_FPS_MASK
1133+
fps |= 0b00000000
1134+
send_command(CommandVals.SetFps, [fps])
1135+
set_power_mode_cmd('high')
1136+
elif mode == 'thirtytwo':
1137+
fps = current_fps & ~HIGH_FPS_MASK
1138+
fps |= 0b00010000
1139+
send_command(CommandVals.SetFps, [fps])
1140+
set_power_mode_cmd('high')
1141+
1142+
1143+
def set_power_mode_cmd(mode):
1144+
if mode == 'low':
1145+
send_command(CommandVals.SetPowerMode, [0])
1146+
elif mode == 'high':
1147+
send_command(CommandVals.SetPowerMode, [1])
1148+
else:
1149+
print("Unsupported power mode")
1150+
sys.exit(1)
1151+
1152+
def get_power_mode_cmd():
1153+
res = send_command(CommandVals.SetPowerMode, with_response=True)
1154+
current_mode = int(res[0])
1155+
if current_mode == 0:
1156+
print(f"Current Power Mode: Low Power")
1157+
elif current_mode == 1:
1158+
print(f"Current Power Mode: High Power")
1159+
1160+
def get_fps_cmd():
1161+
res = send_command(CommandVals.SetFps, with_response=True)
1162+
current_fps = res[0]
1163+
res = send_command(CommandVals.SetPowerMode, with_response=True)
1164+
current_mode = int(res[0])
1165+
1166+
if current_mode == 0:
1167+
current_fps &= LOW_FPS_MASK
1168+
if current_fps == 0:
1169+
fps = 0.25
1170+
elif current_fps == 1:
1171+
fps = 0.5
1172+
else:
1173+
fps = 2 ** (current_fps - 2)
1174+
elif current_mode == 1:
1175+
if current_fps & HIGH_FPS_MASK:
1176+
fps = 32
1177+
else:
1178+
fps = 16
1179+
1180+
print(f"Current FPS: {fps}")
1181+
1182+
10761183
# 5x6 symbol font. Leaves 2 pixels on each side empty
10771184
# We can leave one row empty below and then the display fits 5 of these digits.
10781185

fl16-inputmodules/src/control.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use crate::graphics::*;
88
#[cfg(feature = "b1display")]
99
use core::fmt::{Debug, Write};
1010
#[cfg(feature = "b1display")]
11+
use cortex_m::delay::Delay;
12+
#[cfg(feature = "b1display")]
1113
use embedded_graphics::Pixel;
1214
#[cfg(feature = "b1display")]
1315
use embedded_graphics::{
@@ -22,7 +24,7 @@ use embedded_hal::digital::v2::OutputPin;
2224
#[cfg(feature = "b1display")]
2325
use heapless::String;
2426
#[cfg(feature = "b1display")]
25-
use st7306_lcd::ST7306;
27+
use st7306_lcd::{FpsConfig, PowerMode, ST7306};
2628

2729
#[cfg(feature = "ledmatrix")]
2830
use crate::games::pong;
@@ -59,6 +61,8 @@ pub enum CommandVals {
5961
FlushFramebuffer = 0x17,
6062
ClearRam = 0x18,
6163
ScreenSaver = 0x19,
64+
SetFps = 0x1A,
65+
SetPowerMode = 0x1B,
6266
Version = 0x20,
6367
}
6468

@@ -162,6 +166,10 @@ pub enum Command {
162166
ClearRam,
163167
ScreenSaver(bool),
164168
GetScreenSaver,
169+
SetFps(u8),
170+
GetFps,
171+
SetPowerMode(u8),
172+
GetPowerMode,
165173
_Unknown,
166174
}
167175

@@ -200,6 +208,8 @@ pub struct B1DIsplayState {
200208
pub screen_inverted: bool,
201209
pub screen_on: bool,
202210
pub screensaver: Option<ScreenSaverState>,
211+
pub power_mode: PowerMode,
212+
pub fps_config: FpsConfig,
203213
}
204214

205215
pub fn parse_command(count: usize, buf: &[u8]) -> Option<Command> {
@@ -388,6 +398,16 @@ pub fn parse_module_command(count: usize, buf: &[u8]) -> Option<Command> {
388398
} else {
389399
Command::GetScreenSaver
390400
}),
401+
Some(CommandVals::SetFps) => Some(if let Some(fps) = arg {
402+
Command::SetFps(fps)
403+
} else {
404+
Command::GetFps
405+
}),
406+
Some(CommandVals::SetPowerMode) => Some(if let Some(mode) = arg {
407+
Command::SetPowerMode(mode)
408+
} else {
409+
Command::GetPowerMode
410+
}),
391411
_ => None,
392412
}
393413
} else {
@@ -530,6 +550,7 @@ pub fn handle_command<SPI, DC, CS, RST, const COLS: usize, const ROWS: usize>(
530550
state: &mut B1DIsplayState,
531551
logo_rect: Rectangle,
532552
disp: &mut ST7306<SPI, DC, CS, RST, COLS, ROWS>,
553+
delay: &mut Delay,
533554
) -> Option<[u8; 32]>
534555
where
535556
SPI: spi::Write<u8>,
@@ -641,6 +662,41 @@ where
641662
response[0] = state.screensaver.is_some() as u8;
642663
Some(response)
643664
}
665+
Command::SetFps(fps) => {
666+
if let Some(fps_config) = FpsConfig::from_u8(*fps) {
667+
state.fps_config = fps_config;
668+
disp.set_fps(state.fps_config).unwrap();
669+
// TODO: Need to reinit the display
670+
}
671+
None
672+
}
673+
Command::GetFps => {
674+
let mut response: [u8; 32] = [0; 32];
675+
response[0] = state.fps_config.as_u8();
676+
Some(response)
677+
}
678+
Command::SetPowerMode(mode) => {
679+
match mode {
680+
0 => {
681+
state.power_mode = PowerMode::Lpm;
682+
disp.switch_mode(delay, state.power_mode).unwrap();
683+
}
684+
1 => {
685+
state.power_mode = PowerMode::Hpm;
686+
disp.switch_mode(delay, state.power_mode).unwrap();
687+
}
688+
_ => {}
689+
}
690+
None
691+
}
692+
Command::GetPowerMode => {
693+
let mut response: [u8; 32] = [0; 32];
694+
response[0] = match state.power_mode {
695+
PowerMode::Lpm => 0,
696+
PowerMode::Hpm => 1,
697+
};
698+
Some(response)
699+
}
644700
_ => handle_generic_command(command),
645701
}
646702
}

inputmodule-control/src/b1display.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ pub enum B1Pattern {
77
//Checkerboard,
88
}
99

10+
#[derive(Copy, Clone, Debug, PartialEq, clap::ValueEnum)]
11+
pub enum Fps {
12+
Quarter,
13+
Half,
14+
One,
15+
Two,
16+
Four,
17+
Eight,
18+
Sixteen,
19+
ThirtyTwo,
20+
}
21+
22+
#[derive(Copy, Clone, Debug, PartialEq, clap::ValueEnum)]
23+
pub enum PowerMode {
24+
Low,
25+
High,
26+
}
27+
1028
/// B1 Display
1129
#[derive(Parser, Debug)]
1230
#[command(arg_required_else_help = true)]
@@ -45,6 +63,15 @@ pub struct B1DisplaySubcommand {
4563
#[arg(long)]
4664
pub screen_saver: Option<Option<bool>>,
4765

66+
/// Set/get FPS
67+
#[arg(long)]
68+
#[clap(value_enum)]
69+
pub fps: Option<Option<Fps>>,
70+
71+
/// Set/get power mode
72+
#[arg(long)]
73+
pub power_mode: Option<Option<PowerMode>>,
74+
4875
/// Display black&white image (300x400px)
4976
#[arg(long)]
5077
pub image_bw: Option<String>,

0 commit comments

Comments
 (0)