diff options
author | Marc Poulhiès <dkm@kataplop.net> | 2020-12-22 22:40:03 +0100 |
---|---|---|
committer | Marc Poulhiès <dkm@kataplop.net> | 2021-01-10 15:39:19 +0100 |
commit | 4e71a432f3729e1477e362b1a302b13ad80575f8 (patch) | |
tree | 63a00ac78bb8a6ecdc1fb9fd0a3ed13b1316f47d /firmware | |
parent | f26c24e094dcd8634a9333ae0048e0688e799cc1 (diff) |
Simple backlight support using custom actions.
Use keyberon's custom action to create some useless LEDs mode.
Frequency (when applicable) and color can be changed by some keypress.
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/Cargo.toml | 2 | ||||
-rw-r--r-- | firmware/src/main.rs | 293 |
2 files changed, 271 insertions, 24 deletions
diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index 2fd5d7b..5db1fba 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -10,7 +10,7 @@ stm32f0xx-hal = { git = "https://github.com/dkm/stm32f0xx-hal/", features = ["rt cortex-m = "0.6" cortex-m-rt = { version = "0.6.10", features = ["device"] } panic-halt = "0.2.0" -keyberon = { git = "https://github.com/TeXitoi/keyberon" } +keyberon = { git = "https://github.com/dkm/keyberon" } cortex-m-rtic = "0.5" generic-array = "0.13" embedded-hal = "0.2" diff --git a/firmware/src/main.rs b/firmware/src/main.rs index fa75e5f..614135c 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -12,9 +12,13 @@ use smart_leds::{brightness, colors, SmartLedsWrite, RGB8}; use ws2812_spi as ws2812; use core::convert::Infallible; + use embedded_hal::digital::v2::{InputPin, OutputPin}; + use generic_array::typenum::{U12, U5}; + use hal::gpio::{gpioa, gpiob, Alternate, Input, Output, PullUp, PushPull, AF0}; + use hal::prelude::*; use embedded_hal::spi::FullDuplex; @@ -24,20 +28,22 @@ use hal::{ spi::{EightBit, Mode, Phase, Polarity}, stm32, timers, }; + use keyberon::action::{k, l, m, Action, Action::*}; use keyberon::debounce::Debouncer; use keyberon::impl_heterogenous_array; use keyberon::key_code::KbHidReport; +use keyberon::key_code::KeyCode; use keyberon::key_code::KeyCode::*; -use keyberon::key_code::KeyCode::{self, *}; -use keyberon::layout::{Event, Layout}; +use keyberon::layout::Layout; use keyberon::matrix::{Matrix, PressedKeys}; + use rtic::app; + use stm32f0xx_hal as hal; + use usb_device::bus::UsbBusAllocator; use usb_device::class::UsbClass as _; -use usb_device::device::UsbDeviceState; - type Spi = hal::spi::Spi< stm32::SPI1, gpioa::PA5<Alternate<AF0>>, @@ -97,24 +103,32 @@ impl_heterogenous_array! { [0, 1, 2, 3, 4] } -#[rustfmt::skip] -pub static LAYERS: keyberon::layout::Layers = &[ - &[ - &[k(Grave), k(Kb1),k(Kb2),k(Kb3), k(Kb4),k(Kb5), k(Kb6), k(Kb7), k(Kb8), k(Kb9), k(Kb0), k(Minus), k(Space)], +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum CustomActions { + LightUp, + LightDown, - &[k(Q), k(W), k(E), k(R), k(T), k(Tab), k(Y), k(U), k(I), k(O), k(P), k(LBracket)], - &[k(A), k(S), k(D), k(F), k(G), k(BSpace), k(H), k(J), k(K), k(L), k(SColon), k(Quote)], - &[k(Z), k(X), k(C), k(V), k(B), k(Enter), k(N), k(M), k(Comma), k(Dot), k(Slash), k(Bslash) ], + ModeCycle, + ColorCycle, + FreqUp, + FreqDown, +} - &[k(LCtrl), l(1), k(LGui), k(LShift), k(LAlt), k(Space), k(RAlt), k(RBracket), k(Equal), k(Delete),k(RShift), k(RCtrl)], +#[rustfmt::skip] +pub static LAYERS: keyberon::layout::Layers<CustomActions> = &[ + &[ + &[k(Kb1), k(Kb2), k(Kb3), k(Kb4), k(Kb5), k(Grave), k(Kb6), k(Kb7), k(Kb8), k(Kb9), k(Kb0), k(Minus)], + &[k(Q), k(W), k(E), k(R), k(T), k(Tab), k(Y), k(U), k(I), k(O), k(P), k(LBracket)], + &[k(A), k(S), k(D), k(F), k(G), k(BSpace), k(H), k(J), k(K), k(L), k(SColon), k(Quote)], + &[k(Z), k(X), k(C), k(V), k(B), k(Enter), k(N), k(M), k(Comma), k(Dot), k(Slash), k(Bslash) ], + &[k(LCtrl), l(1), k(LGui), k(LShift), k(LAlt), k(Space), k(RAlt), k(RBracket), k(Equal), k(Delete), k(RShift), k(RCtrl)], ], &[ - &[k(F1),k(F2),k(F3),k(F4),k(F5),k(F6),k(F7),k(F8),k(F9),k(F10),k(F11),k(F12)], - - &[k(SysReq), k(NumLock), Trans, Trans, Trans, k(Escape), k(Insert), k(PgUp), k(PgDown), Trans, Trans, Trans ], - &[Trans , Trans , Trans, Trans, Trans, Trans, k(Home), k(Up), k(End), Trans, Trans, Trans ], - &[k(NonUsBslash), Trans, Trans, Trans, Trans, Trans, k(Left), k(Down), k(Right), Trans, Trans, k(PgUp) ], - &[Trans, Trans, Trans, Trans, Trans, Trans, Trans, Trans, Trans, Trans, Trans, k(PgDown) ], + &[k(F1), k(F2), k(F3), k(F4), k(F5), k(F6), k(F7), k(F8), k(F9), k(F10), k(F11), k(F12)], + &[k(SysReq), k(NumLock), Trans, Trans, Trans, k(Escape), k(Insert), k(PgUp), k(PgDown), Trans, Trans, Trans ], + &[Trans , Trans , Trans, Trans, Trans, Trans, k(Home), k(Up), k(End), Trans, Trans, Trans ], + &[k(NonUsBslash), Action::Custom(CustomActions::ColorCycle), Action::Custom(CustomActions::FreqUp), Action::Custom(CustomActions::FreqDown), Trans, Trans, k(Left), k(Down), k(Right), Trans, Trans, k(PgUp) ], + &[Action::Custom(CustomActions::LightUp), Trans, Action::Custom(CustomActions::LightDown), Action::Custom(CustomActions::ModeCycle), Trans, Trans, Trans, Trans, Trans, Trans, Trans, Trans], ], ]; @@ -155,6 +169,195 @@ where } } +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum BacklightMode { + Off, + Solid(RGB8), + Circling(RGB8, usize, usize, usize, bool), + Breath(RGB8, usize, usize, bool), +} + +pub struct Backlight { + mode: BacklightMode, + brightness: u8, +} + +trait ColorSeq { + fn next_color(&self) -> RGB8; +} + +const COLORS_SEQ: [RGB8; 5] = [ + colors::RED, + colors::GREEN, + colors::BLUE, + colors::VIOLET, + colors::YELLOW, +]; + +impl ColorSeq for RGB8 { + fn next_color(&self) -> RGB8 { + let mut next = false; + + for c in &COLORS_SEQ { + if next { + return *c; + } + + if *self == *c { + next = true; + } + } + + COLORS_SEQ[0] + } +} + +impl Backlight { + pub fn next_mode(&mut self) { + self.mode = match self.mode { + BacklightMode::Off => BacklightMode::Solid(colors::RED), + BacklightMode::Solid(_) => BacklightMode::Circling(colors::RED, 100, 0, 0, true), + BacklightMode::Circling(_, _, _, _, _) => { + BacklightMode::Breath(colors::RED, 10, 0, true) + } + BacklightMode::Breath(_, _, _, _) => BacklightMode::Off, + } + } + + pub fn change_freq(&mut self, up: bool) { + self.mode = match self.mode { + BacklightMode::Breath(c, tstep, step, dir) => { + let tstep = if up { + if tstep - 10 > 10 { + tstep - 10 + } else { + 10 + } + } else { + if tstep + 10 < 1000 { + tstep + 10 + } else { + 1000 + } + }; + BacklightMode::Breath(c, tstep, step, dir) + } + BacklightMode::Circling(c, tstep, step, i, dir) => { + let tstep = if up { + if tstep - 10 > 10 { + tstep - 10 + } else { + 10 + } + } else { + if tstep + 10 < 1000 { + tstep + 10 + } else { + 1000 + } + }; + BacklightMode::Circling(c, tstep, step, i, dir) + } + any => any, + } + } + + pub fn next_color(&mut self) { + self.mode = match self.mode { + BacklightMode::Solid(c) => BacklightMode::Solid(c.next_color()), + BacklightMode::Breath(c, tstep, step, dir) => { + BacklightMode::Breath(c.next_color(), tstep, step, dir) + } + BacklightMode::Circling(c, ts, s, i, dir) => { + BacklightMode::Circling(c.next_color(), ts, s, i, dir) + } + any => any, + } + } + + pub fn refresh_leds(&mut self, leds: &mut Leds<Spi>) { + self.mode = match self.mode { + BacklightMode::Off => { + for l in leds.leds[4..].iter_mut() { + *l = colors::BLACK; + } + BacklightMode::Off + } + + BacklightMode::Solid(c) => { + for l in leds.leds[4..].iter_mut() { + *l = c; + } + BacklightMode::Solid(c) + } + + BacklightMode::Breath(c, tstep, step, dir) => { + let mut step = step + 1; + let mut new_dir = dir; + + if step >= tstep { + step = 0; + + for l in leds.leds[4..].iter_mut() { + *l = c; + } + + if dir { + if self.brightness == 100 { + self.brightness -= 1; + new_dir = false; + } + self.brightness += 1; + } else { + if self.brightness == 5 { + self.brightness += 1; + new_dir = true; + } + self.brightness -= 1; + } + } + + BacklightMode::Breath(c, tstep, step, new_dir) + } + + BacklightMode::Circling(c, tstep, step, index, dir) => { + let mut new_dir = dir; + let mut new_index = index; + + let mut step = step + 1; + + if step >= tstep { + step = 0; + + if new_index == 0 && !dir { + new_index = 0; + new_dir = true; + } else if new_index == 6 && dir { + new_index = 6; + new_dir = false; + } else { + new_index = if dir { index + 1 } else { index - 1 }; + } + } + + for (i, l) in leds.leds[4..].iter_mut().enumerate() { + let ni = if new_index == 0 { 5 } else { new_index - 1 }; + if i == ni { + *l = c; + } else { + *l = colors::BLACK; + } + } + BacklightMode::Circling(c, tstep, step, new_index as usize, new_dir) + } + any => any, + }; + + leds.ws + .write(brightness(leds.leds.iter().cloned(), self.brightness)); + } +} + #[app(device = crate::hal::pac, peripherals = true)] const APP: () = { struct Resources { @@ -162,8 +365,10 @@ const APP: () = { usb_class: UsbClass, matrix: Matrix<Cols, Rows>, debouncer: Debouncer<PressedKeys<U5, U12>>, - layout: Layout, + layout: Layout<CustomActions>, timer: timers::Timer<stm32::TIM3>, + + backlight: Backlight, } #[init] @@ -219,7 +424,7 @@ const APP: () = { ); // ws2812 - let mut ws = ws2812::Ws2812::new(spi); + let ws = ws2812::Ws2812::new(spi); let mut leds = Leds { ws, @@ -270,6 +475,11 @@ const APP: () = { debouncer: Debouncer::new(PressedKeys::default(), PressedKeys::default(), 5), matrix: matrix.get(), layout: Layout::new(LAYERS), + + backlight: Backlight { + mode: BacklightMode::Off, + brightness: 8, + }, } } @@ -283,9 +493,9 @@ const APP: () = { #[task( binds = TIM3, priority = 2, - resources = [matrix, debouncer, timer, layout, usb_class], + resources = [matrix, debouncer, timer, layout, usb_class, backlight], )] - fn tick(mut c: tick::Context) { + fn tick(c: tick::Context) { c.resources.timer.wait().ok(); for event in c @@ -295,12 +505,49 @@ const APP: () = { { c.resources.layout.event(event); } + let mut usb_class = c.resources.usb_class; + let backlight = c.resources.backlight; + match c.resources.layout.tick() { + keyberon::layout::CustomEvent::Release(CustomActions::LightUp) => { + let bl_val = &mut backlight.brightness; + *bl_val = if *bl_val == 100 { 100 } else { *bl_val + 1 }; + usb_class.lock(|k| { + let leds = k.device_mut().leds(); + leds.ws + .write(brightness(leds.leds.iter().cloned(), *bl_val)); + }); + } + keyberon::layout::CustomEvent::Release(CustomActions::LightDown) => { + let bl_val = &mut backlight.brightness; + *bl_val = if *bl_val == 0 { 0 } else { *bl_val - 1 }; + usb_class.lock(|k| { + let leds = k.device_mut().leds(); + leds.ws + .write(brightness(leds.leds.iter().cloned(), *bl_val)); + }); + } + keyberon::layout::CustomEvent::Release(CustomActions::ColorCycle) => { + backlight.next_color(); + } + keyberon::layout::CustomEvent::Release(CustomActions::ModeCycle) => { + backlight.next_mode(); + } + keyberon::layout::CustomEvent::Release(CustomActions::FreqUp) => { + backlight.change_freq(true); + } + keyberon::layout::CustomEvent::Release(CustomActions::FreqDown) => { + backlight.change_freq(false); + } _ => (), } + usb_class.lock(|k| { + backlight.refresh_leds(&mut k.device_mut().leds()); + }); + c.resources.layout.tick(); - send_report(c.resources.layout.keycodes(), &mut c.resources.usb_class); + send_report(c.resources.layout.keycodes(), &mut usb_class); } extern "C" { |