Skip to content

Commit f79ffeb

Browse files
Merge pull request #24 from FrameworkComputer/fps-power-mode
b1display: Allow set/get of FPS and power mode
2 parents e269f16 + 4b64a5a commit f79ffeb

File tree

13 files changed

+508
-48
lines changed

13 files changed

+508
-48
lines changed

b1display/README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,16 @@ Options:
6464
Invert screen on/off [possible values: true, false]
6565
--screen-saver [<SCREEN_SAVER>]
6666
Screensaver on/off [possible values: true, false]
67-
--image-bw <IMAGE_BW>
68-
Display black&white image (300x400px)
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]
71+
--animation-fps [<ANIMATION_FPS>]
72+
Set/get animation FPS
73+
--image <IMAGE>
74+
Display a black&white image (300x400px)
75+
--animated-gif <ANIMATED_GIF>
76+
Display an animated black&white GIF (300x400px)
6977
--clear-ram
7078
Clear display RAM
7179
-h, --help

b1display/src/main.rs

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ use usbd_serial::{SerialPort, USB_CLASS_CDC};
4444

4545
// Used to demonstrate writing formatted strings
4646
use core::fmt::Debug;
47-
//use core::fmt::Write;
48-
//use heapless::String;
47+
use core::fmt::Write;
48+
use heapless::String;
4949

5050
use fl16_inputmodules::control::*;
5151
use fl16_inputmodules::graphics::*;
@@ -68,7 +68,7 @@ type B1ST7306 = ST7306<
6868
>;
6969

7070
const DEBUG: bool = false;
71-
const SCRNS_DELTA: i32 = 10;
71+
const SCRNS_DELTA: i32 = 5;
7272
const WIDTH: i32 = 300;
7373
const HEIGHT: i32 = 400;
7474
const SIZE: Size = Size::new(WIDTH as u32, HEIGHT as u32);
@@ -146,6 +146,19 @@ 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+
animation_period: 1_000_000, // 1000ms = 1Hz
160+
};
161+
149162
const INVERTED: bool = false;
150163
const AUTO_PWRDOWN: bool = true;
151164
const TE_ENABLE: bool = true;
@@ -159,10 +172,7 @@ fn main() -> ! {
159172
INVERTED,
160173
AUTO_PWRDOWN,
161174
TE_ENABLE,
162-
FpsConfig {
163-
hpm: HpmFps::ThirtyTwo,
164-
lpm: LpmFps::One,
165-
},
175+
state.fps_config,
166176
WIDTH as u16,
167177
HEIGHT as u16,
168178
COL_START,
@@ -211,15 +221,9 @@ fn main() -> ! {
211221

212222
let sleep = pins.sleep.into_pull_down_input();
213223

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

224228
let mut logo_pos = Point::new(LOGO_OFFSET_X, LOGO_OFFSET_Y);
225229

@@ -229,10 +233,32 @@ fn main() -> ! {
229233
handle_sleep(host_sleeping, &mut state, &mut delay, &mut disp);
230234

231235
// Handle period display updates. Don't do it too often
232-
if timer.get_counter().ticks() > prev_timer + 500_000 {
236+
if timer.get_counter().ticks() > prev_timer + state.animation_period {
233237
prev_timer = timer.get_counter().ticks();
234238

235239
if let Some(ref mut screensaver) = state.screensaver {
240+
let seconds = ticks / (1_000_000 / state.animation_period);
241+
#[allow(clippy::modulo_one)]
242+
let second_decimals = ticks % (1_000_000 / state.animation_period);
243+
Rectangle::new(Point::new(0, 0), Size::new(300, 50))
244+
.into_styled(PrimitiveStyle::with_fill(Rgb565::WHITE))
245+
.draw(&mut disp)
246+
.unwrap();
247+
let mut text: String<32> = String::new();
248+
write!(
249+
&mut text,
250+
"{:>4} Ticks ({:>4}.{} s)",
251+
ticks, seconds, second_decimals
252+
)
253+
.unwrap();
254+
// Uncomment to draw the ticks on the screen
255+
//draw_text(
256+
// &mut disp,
257+
// &text,
258+
// Point::new(0, 0),
259+
//).unwrap();
260+
ticks += 1;
261+
236262
logo_pos = {
237263
let (x, y) = (logo_pos.x, logo_pos.y);
238264
let w = logo_rect.size.width as i32;
@@ -286,16 +312,16 @@ fn main() -> ! {
286312
(Some(c @ Command::BootloaderReset), _)
287313
| (Some(c @ Command::IsSleeping), _) => {
288314
if let Some(response) =
289-
handle_command(&c, &mut state, logo_rect, &mut disp)
315+
handle_command(&c, &mut state, logo_rect, &mut disp, &mut delay)
290316
{
291317
let _ = serial.write(&response);
292318
};
293319
}
294320
(Some(command), SimpleSleepState::Awake) => {
295321
// 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-
{
322+
if let Some(response) = handle_command(
323+
&command, &mut state, logo_rect, &mut disp, &mut delay,
324+
) {
299325
let _ = serial.write(&response);
300326
};
301327
// Must write AFTER writing response, otherwise the
@@ -352,7 +378,9 @@ fn handle_sleep<SPI, DC, CS, RST, const COLS: usize, const ROWS: usize>(
352378
//disp.on_off(true).unwrap();
353379
disp.sleep_out(delay).unwrap();
354380
// 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();
381+
if state.power_mode == PowerMode::Lpm {
382+
disp.switch_mode(delay, PowerMode::Lpm).unwrap();
383+
}
356384

357385
// Turn screensaver on when resuming from sleep
358386
// 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

0 commit comments

Comments
 (0)