From 629b2d625c80a2c2aa1fc435771a09155385e26a Mon Sep 17 00:00:00 2001 From: Danny August Ramaputra Date: Thu, 5 Sep 2024 22:35:18 +0900 Subject: [PATCH] add: basic oled peripheral --- Cargo.lock | 63 +++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/keyboard/default/mod.rs | 5 +++ src/keyboard/kb_dev/mod.rs | 5 +++ src/keyboard/mod.rs | 20 +++++++++++ src/keyboard/quadax_rift/mod.rs | 31 ++++++++++++++-- src/main.rs | 20 +++++++++-- src/oled.rs | 37 +++++++++++++++++++ src/split.rs | 10 ++++++ 9 files changed, 187 insertions(+), 5 deletions(-) create mode 100644 src/oled.rs diff --git a/Cargo.lock b/Cargo.lock index e8c5f63..226cf52 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -34,6 +34,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" +[[package]] +name = "az" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" + [[package]] name = "bare-metal" version = "0.2.5" @@ -79,6 +85,12 @@ dependencies = [ "wyz", ] +[[package]] +name = "byte-slice-cast" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0a5e3906bcbf133e33c1d4d95afc664ad37fbdb9f6568d8043e7ea8c27d93d3" + [[package]] name = "bytemuck" version = "1.16.1" @@ -198,6 +210,33 @@ dependencies = [ "defmt", ] +[[package]] +name = "display-interface" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7517c040926d7b02b111884aa089177db80878533127f7c1b480d852c5fb4112" + +[[package]] +name = "display-interface-i2c" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4895cd4e54e5536ef370d7f1eec787aad8275dd8ad15815aebfa71dd847b4ebf" +dependencies = [ + "display-interface", + "embedded-hal 0.2.7", +] + +[[package]] +name = "display-interface-spi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489378ad054862146fbd1f09f51d585ccbe4bd1e2feadcda2a13ac33f840e1a5" +dependencies = [ + "byte-slice-cast", + "display-interface", + "embedded-hal 0.2.7", +] + [[package]] name = "either" version = "1.13.0" @@ -223,6 +262,16 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "embedded-graphics-core" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba9ecd261f991856250d2207f6d8376946cd9f412a2165d3b75bc87a0bc7a044" +dependencies = [ + "az", + "byteorder", +] + [[package]] name = "embedded-hal" version = "0.2.7" @@ -481,6 +530,7 @@ dependencies = [ "rtic-sync", "serde", "smart-leds", + "ssd1306", "usb-device", "usbd-human-interface-device", "ws2812-pio", @@ -944,6 +994,19 @@ dependencies = [ "lock_api", ] +[[package]] +name = "ssd1306" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b37918f6137f2b58427181c3e10a731bef1061a9756b13ffbade64f21892acc6" +dependencies = [ + "display-interface", + "display-interface-i2c", + "display-interface-spi", + "embedded-graphics-core", + "embedded-hal 0.2.7", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" diff --git a/Cargo.toml b/Cargo.toml index 78cc9dd..df87757 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ usb-device = "0.3.2" usbd-human-interface-device = "0.5.0" smart-leds = "0.3.0" ws2812-pio = "0.8.0" +ssd1306 = "0.8.4" serde = { version = "1.0.204", default-features = false, features = ["derive"] } postcard = { version = "1.0.8", features = ["alloc"] } diff --git a/src/keyboard/default/mod.rs b/src/keyboard/default/mod.rs index a1baccd..765af57 100644 --- a/src/keyboard/default/mod.rs +++ b/src/keyboard/default/mod.rs @@ -24,6 +24,8 @@ const ENABLE_RGB_MATRIX: bool = true; pub struct Keyboard {} impl Configurator for Keyboard { + const NAME: &str = "default"; + const KEY_MATRIX_ROW_COUNT: usize = 2; const KEY_MATRIX_COL_COUNT: usize = 2; @@ -34,9 +36,11 @@ impl Configurator for Keyboard { mut slices: pwm::Slices, mut pio0: pio::PIO, sm0: pio::UninitStateMachine<(pac::PIO0, pio::SM0)>, + _i2c1: pac::I2C1, _uart0: pac::UART0, _resets: &mut pac::RESETS, clock_freq: HertzU32, + _system_clock: &hal::clocks::SystemClock, ) -> ( Configuration, Option<(Arbiter>>, UartReceiver)>, @@ -94,6 +98,7 @@ impl Configurator for Keyboard { rotary_encoder, heartbeat_led, rgb_matrix, + oled_display: None, }, None, ) diff --git a/src/keyboard/kb_dev/mod.rs b/src/keyboard/kb_dev/mod.rs index b7f6430..cd6f1d5 100644 --- a/src/keyboard/kb_dev/mod.rs +++ b/src/keyboard/kb_dev/mod.rs @@ -24,6 +24,8 @@ const ENABLE_RGB_MATRIX: bool = true; pub struct Keyboard {} impl Configurator for Keyboard { + const NAME: &str = "kb_dev"; + const KEY_MATRIX_ROW_COUNT: usize = 5; const KEY_MATRIX_COL_COUNT: usize = 15; @@ -34,9 +36,11 @@ impl Configurator for Keyboard { mut slices: pwm::Slices, mut pio0: pio::PIO, sm0: pio::UninitStateMachine<(pac::PIO0, pio::SM0)>, + _i2c1: pac::I2C1, _uart0: pac::UART0, _resets: &mut pac::RESETS, clock_freq: HertzU32, + _system_clock: &hal::clocks::SystemClock, ) -> ( Configuration, Option<(Arbiter>>, UartReceiver)>, @@ -110,6 +114,7 @@ impl Configurator for Keyboard { rotary_encoder, heartbeat_led, rgb_matrix, + oled_display: None, }, None, ) diff --git a/src/keyboard/mod.rs b/src/keyboard/mod.rs index 31434c7..057730b 100644 --- a/src/keyboard/mod.rs +++ b/src/keyboard/mod.rs @@ -3,12 +3,14 @@ use core::cell::RefCell; use alloc::rc::Rc; use hal::{fugit::HertzU32, gpio, pac, pio, pwm}; use rtic_sync::arbiter::Arbiter; +use ssd1306::prelude::I2CInterface; use ws2812_pio::Ws2812Direct; use crate::{ heartbeat::HeartbeatLED, key::LayerIndex, matrix::{BasicVerticalSwitchMatrix, SplitSwitchMatrix}, + oled::OLEDDisplay, processor::{events::rgb::RGBMatrix, mapper::InputMap}, remote::transport::uart::{UartReceiver, UartSender}, rotary::RotaryEncoder, @@ -58,6 +60,20 @@ pub struct Configuration { >, >, >, + // TODO: configurable OLED display pinout + pub oled_display: Option< + OLEDDisplay< + I2CInterface< + hal::I2C< + pac::I2C1, + ( + gpio::Pin, + gpio::Pin, + ), + >, + >, + >, + >, } impl Configuration { @@ -67,6 +83,8 @@ impl Configuration { } pub trait Configurator { + const NAME: &str; + const LAYER_COUNT: usize = selected_keyboard::layout::LAYER_COUNT; type Layer: LayerIndex = selected_keyboard::layout::Layer; @@ -80,9 +98,11 @@ pub trait Configurator { slices: pwm::Slices, pio0: pio::PIO, sm0: pio::UninitStateMachine<(pac::PIO0, pio::SM0)>, + i2c1: pac::I2C1, uart0: pac::UART0, resets: &mut pac::RESETS, clock_freq: HertzU32, + system_clock: &hal::clocks::SystemClock, ) -> ( Configuration, Option<(Arbiter>>, UartReceiver)>, diff --git a/src/keyboard/quadax_rift/mod.rs b/src/keyboard/quadax_rift/mod.rs index 519b5f1..95221e9 100644 --- a/src/keyboard/quadax_rift/mod.rs +++ b/src/keyboard/quadax_rift/mod.rs @@ -8,26 +8,31 @@ use hal::{ gpio, pac, pio, pwm, uart, }; use rtic_sync::arbiter::Arbiter; +use ssd1306::{prelude::*, size::DisplaySize128x32, I2CDisplayInterface, Ssd1306}; use ws2812_pio::Ws2812Direct; use crate::{ heartbeat::HeartbeatLED, keyboard::{Configuration, Configurator}, matrix::{BasicVerticalSwitchMatrix, SplitSwitchMatrix}, + oled::OLEDDisplay, processor::events::rgb::RGBMatrix, remote::transport::uart::{UartReceiver, UartSender}, rotary::{Mode, RotaryEncoder}, - split::SideDetector, + split::{self, SideDetector}, }; const ENABLE_HEARTBEAT_LED: bool = true; const ENABLE_KEY_MATRIX: bool = true; const ENABLE_ROTARY_ENCODER: bool = true; const ENABLE_RGB_MATRIX: bool = true; +const ENABLE_OLED_SCREEN: bool = true; pub struct Keyboard {} impl Configurator for Keyboard { + const NAME: &str = "quadax-rift"; + const KEY_MATRIX_ROW_COUNT: usize = 5; const KEY_MATRIX_COL_COUNT: usize = 14; @@ -38,13 +43,17 @@ impl Configurator for Keyboard { mut slices: pwm::Slices, mut pio0: pio::PIO, sm0: pio::UninitStateMachine<(pac::PIO0, pio::SM0)>, + i2c1: pac::I2C1, uart0: pac::UART0, resets: &mut pac::RESETS, clock_freq: HertzU32, + system_clock: &hal::clocks::SystemClock, ) -> ( Configuration, Option<(Arbiter>>, UartReceiver)>, ) { + SideDetector::new(Box::new(pins.gpio2.into_pull_down_input())).detect(); + #[rustfmt::skip] let key_matrix_split = if ENABLE_KEY_MATRIX { Some(SplitSwitchMatrix::new(BasicVerticalSwitchMatrix::new( @@ -99,6 +108,23 @@ impl Configurator for Keyboard { None }; + let oled_display = if ENABLE_OLED_SCREEN { + let sda_pin: gpio::Pin<_, gpio::FunctionI2C, _> = pins.gpio26.reconfigure(); + let scl_pin: gpio::Pin<_, gpio::FunctionI2C, _> = pins.gpio27.reconfigure(); + let i2c = hal::I2C::i2c1(i2c1, sda_pin, scl_pin, 400.kHz(), resets, system_clock); + + Some(OLEDDisplay::new(Ssd1306::new( + I2CDisplayInterface::new(i2c), + DisplaySize128x32, + match split::get_self_side() { + split::Side::Left => DisplayRotation::Rotate180, + split::Side::Right => DisplayRotation::Rotate0, + }, + ))) + } else { + None + }; + let mut uart_peripheral = uart::UartPeripheral::new( uart0, (pins.gpio0.into_function(), pins.gpio1.into_function()), @@ -120,8 +146,6 @@ impl Configurator for Keyboard { let uart_sender = Arbiter::new(Rc::new(RefCell::new(UartSender::new(uart_writer)))); let uart_receiver = UartReceiver::new(uart_reader); - SideDetector::new(Box::new(pins.gpio2.into_pull_down_input())).detect(); - ( Configuration { key_matrix: None, @@ -129,6 +153,7 @@ impl Configurator for Keyboard { rotary_encoder, heartbeat_led, rgb_matrix, + oled_display, }, Some((uart_sender, uart_receiver)), ) diff --git a/src/main.rs b/src/main.rs index 812ddfa..7a466dc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ mod heartbeat; mod key; mod keyboard; mod matrix; +mod oled; mod processor; mod remote; mod rotary; @@ -45,7 +46,7 @@ mod kb { [core::mem::MaybeUninit::uninit(); HEAP_SIZE_BYTES]; use alloc::{boxed::Box, rc::Rc, vec::Vec}; - use core::cell::RefCell; + use core::{cell::RefCell, fmt::Write}; use hal::{ clocks::init_clocks_and_plls, gpio, pac, @@ -194,9 +195,11 @@ mod kb { pwm::Slices::new(ctx.device.PWM, &mut ctx.device.RESETS), pio0, sm0, + ctx.device.I2C1, ctx.device.UART0, &mut ctx.device.RESETS, clocks.peripheral_clock.freq(), + &clocks.system_clock, ); assert!( !config.is_split() || transport.is_some(), @@ -273,9 +276,12 @@ mod kb { frame_sender: Sender<'static, Box, 1>, frame_receiver: Receiver<'static, Box, 1>, seq_sender: Option>, - config: Configuration, + mut config: Configuration, ) { defmt::info!("start_wait_usb()"); + if let Some(ref mut display) = config.oled_display { + display.write_str("kb").unwrap(); + } // Start USB tasks hid_usb_tick::spawn().ok(); @@ -297,6 +303,16 @@ mod kb { split::get_self_side() ); + if let Some(ref mut display) = config.oled_display { + display.clear(); + display + .write_fmt(format_args!( + "{}\n{}", + ::NAME, + split::get_self_mode() + )) + .unwrap(); + } match split::get_self_mode() { split::Mode::Master => { heartbeat::spawn(config.heartbeat_led, 500.millis()).ok(); diff --git a/src/oled.rs b/src/oled.rs new file mode 100644 index 0000000..8bcc763 --- /dev/null +++ b/src/oled.rs @@ -0,0 +1,37 @@ +use ssd1306::{prelude::*, Ssd1306}; + +pub struct OLEDDisplay +where + DI: WriteOnlyDataCommand, +{ + // TODO: support arbitrary modes + display: Ssd1306, +} + +impl OLEDDisplay +where + DI: WriteOnlyDataCommand, +{ + pub fn new(mut display: Ssd1306) -> Self { + display.init().unwrap(); + + let mut oled_display = OLEDDisplay { + display: display.into_terminal_mode(), + }; + oled_display.clear(); + oled_display + } + + pub fn clear(&mut self) { + self.display.clear().unwrap(); + } +} + +impl core::fmt::Write for OLEDDisplay +where + DI: WriteOnlyDataCommand, +{ + fn write_str(&mut self, s: &str) -> Result<(), core::fmt::Error> { + self.display.write_str(s) + } +} diff --git a/src/split.rs b/src/split.rs index 984ea3c..914737a 100644 --- a/src/split.rs +++ b/src/split.rs @@ -1,4 +1,5 @@ use alloc::boxed::Box; +use core::fmt::Display; use defmt::Format; use embedded_hal::digital::InputPin; use hal::gpio; @@ -15,6 +16,15 @@ pub enum Mode { Slave, } +impl Display for Mode { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Mode::Master => write!(f, "Master"), + Mode::Slave => write!(f, "Slave"), + } + } +} + pub struct SideDetector { pin: Box>, }