Skip to content

Commit b3d649a

Browse files
committed
Merge remote-tracking branch 'origin/main' into packaging-process-and-systemd-service
2 parents 5535376 + 78ebefa commit b3d649a

13 files changed

+176
-19
lines changed

Cargo.lock

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,15 @@ Optionally there are is also a [Python script](python.md).
3838

3939
For device specific commands, see their individual documentation pages.
4040

41-
Common commands:
41+
###### Permissions on Linux
42+
To ensure that the input module's port is accessible, install the `udev` rule and trigger a reload:
43+
44+
```
45+
sudo cp release/50-framework-inputmodule.rules /etc/udev/rules.d/
46+
sudo udevadm control --reload && sudo udevadm trigger
47+
```
48+
49+
##### Common commands:
4250

4351
###### Listing available devices
4452

fl16-inputmodules/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ num-derive = "0.3"
3131
num-traits = { version = "0.2", default-features = false }
3232

3333
# LED Matrix
34-
is31fl3741 = { git = "https://github.com/JohnAZoidberg/is31fl3741", branch = "all-at-once-update-deps", optional = true }
34+
is31fl3741 = { git = "https://github.com/JohnAZoidberg/is31fl3741", branch = "merged", optional = true }
3535

3636
# B1 Display
3737
st7306 = { git = "https://github.com/FrameworkComputer/st7306-rs", branch = "update-deps", optional = true }

fl16-inputmodules/src/control.rs

+47
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ use crate::games::snake;
3535
use crate::matrix::*;
3636
#[cfg(feature = "ledmatrix")]
3737
use crate::patterns::*;
38+
#[cfg(feature = "ledmatrix")]
39+
use is31fl3741::PwmFreq;
3840

3941
#[cfg(feature = "c1minimal")]
4042
use smart_leds::{SmartLedsWrite, RGB8};
@@ -66,6 +68,7 @@ pub enum CommandVals {
6668
SetFps = 0x1A,
6769
SetPowerMode = 0x1B,
6870
AnimationPeriod = 0x1C,
71+
PwmFreq = 0x1E,
6972
Version = 0x20,
7073
}
7174

@@ -125,6 +128,30 @@ pub enum DisplayMode {
125128
Hpm = 0x01,
126129
}
127130

131+
#[cfg(feature = "ledmatrix")]
132+
#[derive(Copy, Clone, num_derive::FromPrimitive)]
133+
pub enum PwmFreqArg {
134+
/// 29kHz
135+
P29k = 0x00,
136+
/// 3.6kHz
137+
P3k6 = 0x01,
138+
/// 1.8kHz
139+
P1k8 = 0x02,
140+
/// 900Hz
141+
P900 = 0x03,
142+
}
143+
#[cfg(feature = "ledmatrix")]
144+
impl From<PwmFreqArg> for PwmFreq {
145+
fn from(val: PwmFreqArg) -> Self {
146+
match val {
147+
PwmFreqArg::P29k => PwmFreq::P29k,
148+
PwmFreqArg::P3k6 => PwmFreq::P3k6,
149+
PwmFreqArg::P1k8 => PwmFreq::P1k8,
150+
PwmFreqArg::P900 => PwmFreq::P900,
151+
}
152+
}
153+
}
154+
128155
// TODO: Reduce size for modules that don't require other commands
129156
pub enum Command {
130157
/// Get current brightness scaling
@@ -175,6 +202,9 @@ pub enum Command {
175202
GetPowerMode,
176203
SetAnimationPeriod(u16),
177204
GetAnimationPeriod,
205+
#[cfg(feature = "ledmatrix")]
206+
SetPwmFreq(PwmFreqArg),
207+
GetPwmFreq,
178208
_Unknown,
179209
}
180210

@@ -348,6 +378,13 @@ pub fn parse_module_command(count: usize, buf: &[u8]) -> Option<Command> {
348378
Some(Command::GetAnimationPeriod)
349379
}
350380
}
381+
Some(CommandVals::PwmFreq) => {
382+
if let Some(freq) = arg {
383+
FromPrimitive::from_u8(freq).map(Command::SetPwmFreq)
384+
} else {
385+
Some(Command::GetPwmFreq)
386+
}
387+
}
351388
_ => None,
352389
}
353390
} else {
@@ -567,6 +604,16 @@ pub fn handle_command(
567604
response[0..2].copy_from_slice(&(period_ms as u16).to_le_bytes());
568605
Some(response)
569606
}
607+
Command::SetPwmFreq(arg) => {
608+
state.pwm_freq = *arg;
609+
matrix.device.set_pwm_freq(state.pwm_freq.into()).unwrap();
610+
None
611+
}
612+
Command::GetPwmFreq => {
613+
let mut response: [u8; 32] = [0; 32];
614+
response[0] = state.pwm_freq as u8;
615+
Some(response)
616+
}
570617
_ => handle_generic_command(command),
571618
}
572619
}

fl16-inputmodules/src/matrix.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::control::PwmFreqArg;
12
use crate::games::game_of_life::GameOfLifeState;
23
use crate::games::pong::PongState;
34
use crate::games::snake::SnakeState;
@@ -22,6 +23,7 @@ pub struct LedmatrixState {
2223
pub sleeping: SleepState,
2324
pub game: Option<GameState>,
2425
pub animation_period: u64,
26+
pub pwm_freq: PwmFreqArg,
2527
}
2628

2729
#[allow(clippy::large_enum_variant)]

inputmodule-control/src/inputmodule.rs

+38-1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ enum Command {
4747
Fps = 0x1A,
4848
PowerMode = 0x1B,
4949
AnimationPeriod = 0x1C,
50+
PwmFreq = 0x1E,
5051
Version = 0x20,
5152
}
5253

@@ -221,6 +222,10 @@ pub fn serial_commands(args: &crate::ClapCli) {
221222
animation_fps_cmd(serialdev, fps);
222223
}
223224

225+
if let Some(freq) = ledmatrix_args.pwm_freq {
226+
pwm_freq_cmd(serialdev, freq);
227+
}
228+
224229
if ledmatrix_args.stop_game {
225230
simple_cmd(
226231
serialdev,
@@ -980,12 +985,44 @@ fn animation_fps_cmd(serialdev: &str, arg: Option<u16>) {
980985
port.read_exact(response.as_mut_slice())
981986
.expect("Found no data!");
982987

983-
println!("Response: {:X?}", response);
984988
let period = u16::from_le_bytes([response[0], response[1]]);
985989
println!("Animation Frequency: {}ms / {}Hz", period, 1_000 / period);
986990
}
987991
}
988992

993+
fn pwm_freq_cmd(serialdev: &str, arg: Option<u16>) {
994+
let mut port = serialport::new(serialdev, 115_200)
995+
.timeout(SERIAL_TIMEOUT)
996+
.open()
997+
.expect("Failed to open port");
998+
999+
if let Some(freq) = arg {
1000+
let hz = match freq {
1001+
29000 => 0,
1002+
3600 => 1,
1003+
1800 => 2,
1004+
900 => 3,
1005+
_ => panic!("Invalid frequency"),
1006+
};
1007+
simple_cmd_port(&mut port, Command::PwmFreq, &[hz]);
1008+
} else {
1009+
simple_cmd_port(&mut port, Command::PwmFreq, &[]);
1010+
1011+
let mut response: Vec<u8> = vec![0; 32];
1012+
port.read_exact(response.as_mut_slice())
1013+
.expect("Found no data!");
1014+
1015+
let hz = match response[0] {
1016+
0 => 29000,
1017+
1 => 3600,
1018+
2 => 1800,
1019+
3 => 900,
1020+
_ => panic!("Invalid frequency"),
1021+
};
1022+
println!("Animation Frequency: {}Hz", hz);
1023+
}
1024+
}
1025+
9891026
fn set_color_cmd(serialdev: &str, color: Color) {
9901027
let args = match color {
9911028
Color::White => &[0xFF, 0xFF, 0xFF],

inputmodule-control/src/ledmatrix.rs

+5
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ pub struct LedMatrixSubcommand {
126126
#[arg(long)]
127127
pub animation_fps: Option<Option<u16>>,
128128

129+
/// Set/get PWM Frequency in Hz
130+
#[arg(long)]
131+
#[clap(value_enum)]
132+
pub pwm_freq: Option<Option<u16>>,
133+
129134
/// Crash the firmware (TESTING ONLY!)
130135
#[arg(long)]
131136
pub panic: bool,

ledmatrix/50-framework.rules

-2
This file was deleted.

ledmatrix/Cargo.toml

+4-4
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,19 @@ defmt-rtt = "0.4"
1515
rp2040-panic-usb-boot = "0.5.0"
1616

1717
# Not using an external BSP, we've got the Framework Laptop 16 BSPs locally in this crate
18-
rp2040-hal = { version="0.8", features=["rt", "critical-section-impl"] }
18+
rp2040-hal = { version = "0.8", features = ["rt", "critical-section-impl"] }
1919
rp2040-boot2 = "0.3"
2020

2121
# USB Serial
22-
usb-device= "0.2.9"
22+
usb-device = "0.2.9"
2323

2424
heapless = "0.7.16"
2525
usbd-serial = "0.1.1"
2626
usbd-hid = "0.6.1"
2727
fugit = "0.3.7"
2828

29-
is31fl3741 = { git = "https://github.com/JohnAZoidberg/is31fl3741", branch = "all-at-once-update-deps", optional = true }
29+
is31fl3741 = { git = "https://github.com/JohnAZoidberg/is31fl3741", branch = "merged" }
3030

3131
[dependencies.fl16-inputmodules]
3232
path = "../fl16-inputmodules"
33-
features = [ "ledmatrix" ]
33+
features = ["ledmatrix"]

ledmatrix/README.md

-9
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,6 @@ Connection to the host system is via USB 2.0 and currently there is a USB Serial
1515
- Sleep Mode
1616
- Transition slowly turns off/on the LEDs
1717

18-
## Setup
19-
20-
To ensure that the input module's port is accessible, install the `udev` rule for it located in the `50-framework.rules` file.
21-
22-
1. Copy it to your udev rules directory, which is usually at: `/etc/udev/rules.d/`
23-
2. Reload and apply the rules with `sudo udevadm control --reload && sudo udevadm trigger`
24-
25-
You can debug issues using the `udevadm monitor --environment` command.
26-
2718
## Controlling
2819

2920
### Commandline

ledmatrix/src/main.rs

+3
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ fn main() -> ! {
222222
sleeping: SleepState::Awake,
223223
game: None,
224224
animation_period: 31_250, // 31,250 us = 32 FPS
225+
pwm_freq: PwmFreqArg::P29k,
225226
};
226227

227228
let mut matrix = LedMatrix::configure(i2c);
@@ -233,6 +234,8 @@ fn main() -> ! {
233234
.set_scaling(MAX_BRIGHTNESS)
234235
.expect("failed to set scaling");
235236

237+
matrix.device.set_pwm_freq(state.pwm_freq.into()).unwrap();
238+
236239
fill_grid_pixels(&state, &mut matrix);
237240

238241
let timer = Timer::new(pac.TIMER, &mut pac.RESETS);

ledmatrix_control.py

+58
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class CommandVals(IntEnum):
4646
ScreenSaver = 0x19
4747
SetFps = 0x1A
4848
SetPowerMode = 0x1B
49+
PwmFreq = 0x1E
4950
Version = 0x20
5051

5152

@@ -96,6 +97,12 @@ class GameControlVal(IntEnum):
9697
Right = 3
9798
Quit = 4
9899

100+
PWM_FREQUENCIES = [
101+
'29kHz',
102+
'3.6kHz',
103+
'1.8kHz',
104+
'900Hz',
105+
]
99106

100107
PATTERNS = [
101108
'All LEDs on',
@@ -166,6 +173,10 @@ def main():
166173
help='Start/stop vertical scrolling')
167174
parser.add_argument('--get-animate', action='store_true',
168175
help='Check if currently animating')
176+
parser.add_argument("--pwm", help="Adjust the PWM frequency. Value 0-255",
177+
type=int, choices=[29000, 3600, 1800, 900])
178+
parser.add_argument("--get-pwm", help="Get current PWM Frequency",
179+
action="store_true")
169180
parser.add_argument("--pattern", help='Display a pattern',
170181
type=str, choices=PATTERNS)
171182
parser.add_argument("--image", help="Display a PNG or GIF image in black and white only)",
@@ -285,6 +296,18 @@ def main():
285296
elif args.get_brightness:
286297
br = get_brightness(dev)
287298
print(f"Current brightness: {br}")
299+
elif args.pwm is not None:
300+
if args.pwm == 29000:
301+
pwm_freq(dev, '29kHz')
302+
elif args.pwm == 3600:
303+
pwm_freq(dev, '3.6kHz')
304+
elif args.pwm == 1800:
305+
pwm_freq(dev, '1.8kHz')
306+
elif args.pwm == 900:
307+
pwm_freq(dev, '900Hz')
308+
elif args.get_pwm:
309+
p = get_pwm_freq(dev)
310+
print(f"Current PWM Frequency: {p} Hz")
288311
elif args.percentage is not None:
289312
if args.percentage > 100 or args.percentage < 0:
290313
print("Percentage must be 0-100")
@@ -414,6 +437,23 @@ def get_brightness(dev):
414437
return int(res[0])
415438

416439

440+
def get_pwm_freq(dev):
441+
"""Adjust the brightness scaling of the entire screen.
442+
"""
443+
res = send_command(dev, CommandVals.PwmFreq, with_response=True)
444+
freq = int(res[0])
445+
if freq == 0:
446+
return 29000
447+
elif freq == 1:
448+
return 3600
449+
elif freq == 2:
450+
return 1800
451+
elif freq == 3:
452+
return 900
453+
else:
454+
return None
455+
456+
417457
def get_version(dev):
418458
"""Get the device's firmware version"""
419459
res = send_command(dev, CommandVals.Version, with_response=True)
@@ -963,6 +1003,18 @@ def light_leds(dev, leds):
9631003
send_command(dev, CommandVals.Draw, vals)
9641004

9651005

1006+
def pwm_freq(dev, freq):
1007+
"""Display a pattern that's already programmed into the firmware"""
1008+
if freq == '29kHz':
1009+
send_command(dev, CommandVals.PwmFreq, [0])
1010+
elif freq == '3.6kHz':
1011+
send_command(dev, CommandVals.PwmFreq, [1])
1012+
elif freq == '1.8kHz':
1013+
send_command(dev, CommandVals.PwmFreq, [2])
1014+
elif freq == '900Hz':
1015+
send_command(dev, CommandVals.PwmFreq, [3])
1016+
1017+
9661018
def pattern(dev, p):
9671019
"""Display a pattern that's already programmed into the firmware"""
9681020
if p == 'All LEDs on':
@@ -1177,6 +1229,9 @@ def gui(devices):
11771229
[sg.Button("Send '2 5 degC thunder'", k='-SEND-TEXT-')],
11781230
])
11791231
],
1232+
[sg.HorizontalSeparator()],
1233+
[sg.Text("PWM Frequency")],
1234+
[sg.Combo(PWM_FREQUENCIES, k='-PWM-FREQ-', enable_events=True)],
11801235

11811236

11821237
# TODO
@@ -1245,6 +1300,9 @@ def gui(devices):
12451300
if event == '-PATTERN-':
12461301
pattern(dev, values['-PATTERN-'])
12471302

1303+
if event == '-PWM-FREQ-':
1304+
pwm_freq(dev, values['-PWM-FREQ-'])
1305+
12481306
if event == 'Start Animation':
12491307
animate(dev, True)
12501308

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Framework Laptop 16 - LED Matrix
2+
SUBSYSTEMS=="usb", ATTRS{idVendor}=="32ac", ATTRS{idProduct}=="0020", MODE="0660", TAG+="uaccess"
3+
4+
# B1 Display (Experimental prototype, not a product)
5+
SUBSYSTEMS=="usb", ATTRS{idVendor}=="32ac", ATTRS{idProduct}=="0021", MODE="0660", TAG+="uaccess"
6+
7+
# C1 Minimal Microcontroller Module (Template for DIY Module)
8+
SUBSYSTEMS=="usb", ATTRS{idVendor}=="32ac", ATTRS{idProduct}=="0022", MODE="0660", TAG+="uaccess"

0 commit comments

Comments
 (0)