Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions rumcake-macros/src/hw/stm32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ crate::parse_as_custom_fields! {
i2c: Ident,
scl: Ident,
sda: Ident,
rx_dma: Ident,
tx_dma: Ident
rx_dma: OptionalItem<Ident>,
tx_dma: OptionalItem<Ident>
}
}

Expand All @@ -93,6 +93,26 @@ pub fn setup_i2c(
}
};

let rx_dma = if let OptionalItem::Some(rx_dma) = rx_dma {
quote! {
::rumcake::hw::platform::embassy_stm32::peripherals::#rx_dma::steal()
}
} else {
quote! {
::rumcake::hw::platform::embassy_stm32::dma::NoDma
}
};

let tx_dma = if let OptionalItem::Some(tx_dma) = tx_dma {
quote! {
::rumcake::hw::platform::embassy_stm32::peripherals::#tx_dma::steal()
}
} else {
quote! {
::rumcake::hw::platform::embassy_stm32::dma::NoDma
}
};

quote! {
unsafe {
::rumcake::hw::platform::embassy_stm32::bind_interrupts! {
Expand All @@ -103,8 +123,8 @@ pub fn setup_i2c(
let i2c = ::rumcake::hw::platform::embassy_stm32::peripherals::#i2c::steal();
let scl = ::rumcake::hw::platform::embassy_stm32::peripherals::#scl::steal();
let sda = ::rumcake::hw::platform::embassy_stm32::peripherals::#sda::steal();
let rx_dma = ::rumcake::hw::platform::embassy_stm32::peripherals::#rx_dma::steal();
let tx_dma = ::rumcake::hw::platform::embassy_stm32::peripherals::#tx_dma::steal();
let rx_dma = #rx_dma;
let tx_dma = #tx_dma;
let time = ::rumcake::hw::platform::embassy_stm32::time::Hertz(100_000);
::rumcake::hw::platform::embassy_stm32::i2c::I2c::new(i2c, scl, sda, Irqs, tx_dma, rx_dma, time, Default::default())
}
Expand Down
32 changes: 32 additions & 0 deletions rumcake-macros/src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ pub(crate) struct KeyboardSettings {
bluetooth: bool,
usb: bool,
encoders: bool,
pointer: Option<PointerSettings>,
storage: Option<StorageSettings>,
simple_backlight: Option<LightingSettings>,
simple_backlight_matrix: Option<LightingSettings>,
Expand All @@ -32,6 +33,11 @@ pub(crate) struct KeyboardSettings {
bootloader_double_tap_reset: Option<Override<LitInt>>,
}

#[derive(Debug, FromMeta)]
pub(crate) struct PointerSettings {
driver_setup_fn: Ident,
}

#[derive(Debug, FromMeta)]
pub(crate) struct LightingSettings {
id: Ident,
Expand Down Expand Up @@ -327,6 +333,12 @@ pub(crate) fn keyboard_main(
spawning.extend(quote! {
spawner.spawn(::rumcake::layout_collect!(#kb_name)).unwrap();
});

if keyboard.pointer.is_some() || keyboard.split_central.is_some() {
spawning.extend(quote! {
spawner.spawn(::rumcake::collect_mouse_events!(#kb_name)).unwrap();
})
}
}

spawning.extend(quote! {
Expand Down Expand Up @@ -374,6 +386,26 @@ pub(crate) fn keyboard_main(
}
}

if keyboard.usb && (keyboard.pointer.is_some() || keyboard.split_central.is_some()) {
initialization.extend(quote! {
let pointer_class = ::rumcake::usb::setup_usb_hid_mouse_writer(&mut builder);
});
spawning.extend(quote! {
spawner.spawn(::rumcake::usb_hid_mouse_write_task!(#kb_name, pointer_class)).unwrap();
});
}

if let Some(args) = keyboard.pointer {
let setup_fn = args.driver_setup_fn;
initialization.extend(quote! {
let pointer_driver = #setup_fn().await;
});
spawning.extend(quote! {
// HID Consumer Report sending
spawner.spawn(::rumcake::poll_pointing_device!(#kb_name, pointer_driver)).unwrap();
});
}

if keyboard.usb && (keyboard.via.is_some() || keyboard.vial.is_some()) {
initialization.extend(quote! {
static VIA_COMMAND_HANDLER: ::rumcake::usb::ViaCommandHandler<#kb_name> = ::rumcake::usb::ViaCommandHandler::new();
Expand Down
2 changes: 2 additions & 0 deletions rumcake/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ smart-leds = "0.3.0"
# third party drivers
is31fl3731 = { git = "https://github.com/Univa/is31fl3731", features = ["async"], optional = true }
ssd1306 = { version = "0.8.2", optional = true }
iqs5xx = { version = "0.1.2", optional = true }

rumcake-macros = { path = "../rumcake-macros" }

Expand Down Expand Up @@ -135,4 +136,5 @@ split-central = ["nrf-softdevice?/ble-central", "nrf-softdevice?/ble-gatt-client
ws2812-bitbang = []
is31fl3731 = ["dep:is31fl3731"]
ssd1306 = ["dep:ssd1306"]
iqs5xx = ["dep:iqs5xx"]

139 changes: 139 additions & 0 deletions rumcake/src/drivers/iqs5xx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
//! Rumcaker driver implementations for [rwalkr's IQS5xx driver](`iqs5xx`)
//!
//! This provides implementations for [`PointingDriver`](`crate::pointer::PointingDriver`).
//!
//! To use this driver as a pointing device, you must implement [`IQS5xxPointerDriver`], and pass
//! it to [`setup_driver`]. Then the result of this can be passed to the [`poll_pointing_device`]
//! task.

use defmt::Debug2Format;
use embassy_time::Delay;
use embedded_hal::blocking::i2c::{Write, WriteRead};
use embedded_hal::digital::v2::{InputPin, OutputPin};
pub use iqs5xx;
use iqs5xx::{Event, IQS5xx as IQS5xxDriver, Report};

use crate::pointer::mouse::{MouseButtonFlags, MouseEvent};
use crate::pointer::touchpad::{Touchpad, TouchpadEvent};
use crate::pointer::PointingDriver;

pub struct IQS5xx<E, I2C, RDY, RST> {
driver: IQS5xxDriver<I2C, RDY, RST>,
touchpad_state: Touchpad,
event_handler: E,
}

pub struct DefaultBehavior;
impl IQS5xxEventHandler for DefaultBehavior {}

pub fn setup_driver<I2C: Write + WriteRead, RDY: InputPin, RST: OutputPin>(
i2c: I2C,
rdy: RDY,
rst: RST,
) -> IQS5xx<DefaultBehavior, I2C, RDY, RST> {
let mut iqs = IQS5xx {
driver: IQS5xxDriver::new(i2c, 0, rdy, rst),
event_handler: DefaultBehavior,
touchpad_state: Touchpad::new(),
};
iqs.driver.reset(&mut Delay).unwrap();
iqs.driver.poll_ready(&mut Delay).unwrap();
iqs.driver.init().unwrap();
iqs
}

pub fn setup_driver_with_custom_behavior<
E,
I2C: Write + WriteRead,
RDY: InputPin,
RST: OutputPin,
>(
i2c: I2C,
rdy: RDY,
rst: RST,
event_handler: E,
) -> IQS5xx<E, I2C, RDY, RST> {
let mut iqs = IQS5xx {
driver: IQS5xxDriver::new(i2c, 0, rdy, rst),
event_handler,
touchpad_state: Touchpad::new(),
};
iqs.driver.reset(&mut Delay).unwrap();
iqs.driver.poll_ready(&mut Delay).unwrap();
iqs.driver.init().unwrap();
iqs
}

pub trait IQS5xxEventHandler {
/// This function gets called at a regular interval (usually every millisecond). You can
/// re-implement this if you the type you're implementing this trait on needs to update its
/// state over time. This can be useful if you want to implement more complicated touchpad
/// functionality which isn't already supported by the [`Touchpad`] struct.
fn tick(&mut self) {}

fn handle_event(&mut self, state: &mut Touchpad, _report: Report, event: Event) {
match event {
iqs5xx::Event::Move { x, y } => {
state.register(TouchpadEvent::Movement(x as i8, y as i8));
}
iqs5xx::Event::SingleTap { x, y } => {
state.register(TouchpadEvent::Tap(MouseButtonFlags::LEFT));
}
iqs5xx::Event::PressHold { x, y } => {
state.register(TouchpadEvent::Hold(MouseButtonFlags::LEFT));
state.register(TouchpadEvent::Movement(x as i8, y as i8));
}
iqs5xx::Event::TwoFingerTap => {
state.register(TouchpadEvent::Tap(MouseButtonFlags::RIGHT));
}
iqs5xx::Event::Scroll { x, y: _ } if x != 0 => {
state.register(TouchpadEvent::Scroll(x as i8, 0));
}
iqs5xx::Event::Scroll { x: _, y } if y != 0 => {
state.register(TouchpadEvent::Scroll(0, y as i8));
}
_ => {}
};
}
}

impl<E: IQS5xxEventHandler, I2C: Write + WriteRead, RDY, RST> IQS5xx<E, I2C, RDY, RST>
where
RDY: InputPin,
RST: OutputPin,
{
async fn tick(&mut self) -> impl Iterator<Item = MouseEvent> + '_ {
self.touchpad_state.tick();
self.event_handler.tick();

let report = self.driver.try_transact(|driver| driver.get_report());

match report {
Ok(Some(report)) => {
let event = iqs5xx::Event::from(&report);
self.event_handler
.handle_event(&mut self.touchpad_state, report, event);
}
Err(error) => {
defmt::warn!(
"[IQS5XX_DRIVER] Could not get report: {}",
Debug2Format(&error)
);
}
_ => {}
}

self.touchpad_state.events()
}
}

impl<E: IQS5xxEventHandler, I2C: Write + WriteRead, RDY, RST> PointingDriver
for IQS5xx<E, I2C, RDY, RST>
where
RDY: InputPin,
RST: OutputPin,
{
async fn tick(&mut self) -> impl Iterator<Item = MouseEvent> {
self.tick().await
}
}
3 changes: 3 additions & 0 deletions rumcake/src/drivers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ pub mod ssd1306;
#[cfg(feature = "ws2812-bitbang")]
pub mod ws2812_bitbang;

#[cfg(feature = "iqs5xx")]
pub mod iqs5xx;

/// Struct that allows you to use a serial driver (implementor of both [`embedded_io_async::Read`]
/// and [`embedded_io_async::Write`]) with rumcake. This can be used for split keyboards.
pub struct SerialSplitDriver<D: Write + Read> {
Expand Down
9 changes: 9 additions & 0 deletions rumcake/src/hw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use embedded_hal::digital::v2::OutputPin;
use platform::RawMutex;
use usbd_human_interface_device::device::consumer::MultipleConsumerReport;
use usbd_human_interface_device::device::keyboard::NKROBootKeyboardReport;
use usbd_human_interface_device::device::mouse::WheelMouseReport;

/// State that contains the current battery level. `rumcake` may or may not use this static
/// internally, depending on what MCU is being used. The contents of this state is usually set by a
Expand Down Expand Up @@ -86,6 +87,8 @@ pub static CURRENT_OUTPUT_STATE: State<Option<HIDOutput>> = State::new(
&crate::usb::KB_CURRENT_OUTPUT_STATE_LISTENER,
#[cfg(feature = "usb")]
&crate::usb::CONSUMER_CURRENT_OUTPUT_STATE_LISTENER,
#[cfg(feature = "usb")]
&crate::usb::MOUSE_CURRENT_OUTPUT_STATE_LISTENER,
#[cfg(all(feature = "usb", feature = "via"))]
&crate::usb::VIA_CURRENT_OUTPUT_STATE_LISTENER,
#[cfg(feature = "bluetooth")]
Expand Down Expand Up @@ -276,6 +279,12 @@ pub trait HIDDevice {
&CONSUMER_REPORT_HID_SEND_CHANNEL
}

fn get_mouse_report_send_channel() -> &'static Channel<RawMutex, WheelMouseReport, 1> {
static MOUSE_REPORT_HID_SEND_CHANNEL: Channel<RawMutex, WheelMouseReport, 1> =
Channel::new();
&MOUSE_REPORT_HID_SEND_CHANNEL
}

#[cfg(feature = "via")]
fn get_via_hid_send_channel() -> &'static Channel<RawMutex, [u8; 32], 1> {
static VIA_REPORT_HID_SEND_CHANNEL: Channel<RawMutex, [u8; 32], 1> = Channel::new();
Expand Down
7 changes: 4 additions & 3 deletions rumcake/src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ pub async fn matrix_poll<K: KeyboardMatrix + 'static>(_k: K) {
let layout_channel = <K::Layout as private::MaybeKeyboardLayout>::get_matrix_events_channel();

#[cfg(feature = "split-peripheral")]
let peripheral_channel = <K::PeripheralDeviceType as crate::split::peripheral::private::MaybePeripheralDevice>::get_matrix_events_channel();
let peripheral_channel = <K::PeripheralDeviceType as crate::split::peripheral::private::MaybePeripheralDevice>::get_message_to_central_channel();

loop {
{
Expand All @@ -502,13 +502,15 @@ pub async fn matrix_poll<K: KeyboardMatrix + 'static>(_k: K) {
Debug2Format(&remapped_event)
);

MATRIX_EVENTS.publish_immediate(remapped_event);

if let Some(layout_channel) = layout_channel {
layout_channel.send(remapped_event).await
};

#[cfg(feature = "split-peripheral")]
if let Some(peripheral_channel) = peripheral_channel {
peripheral_channel.send(remapped_event).await
peripheral_channel.send(remapped_event.into()).await
};
}
}
Expand Down Expand Up @@ -552,7 +554,6 @@ where

if let Ok(event) = matrix_channel.try_receive() {
layout.event(event);
MATRIX_EVENTS.publish_immediate(event); // Just immediately publish since we don't want to hold up any key events to be converted into keycodes.
};

let tick = layout.tick();
Expand Down
8 changes: 7 additions & 1 deletion rumcake/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ pub use once_cell;
pub use rumcake_macros::keyboard_main as keyboard;

pub mod keyboard;
pub mod pointer;

mod math;

#[cfg(feature = "storage")]
Expand Down Expand Up @@ -148,6 +150,7 @@ pub mod drivers;
pub mod tasks {
pub use crate::hw::__output_switcher;
pub use crate::keyboard::{__ec11_encoders_poll, __layout_collect, __matrix_poll};
pub use crate::pointer::{__collect_mouse_events, __poll_pointing_device};

#[cfg(all(feature = "lighting", feature = "storage"))]
pub use crate::lighting::__lighting_storage_task;
Expand All @@ -158,7 +161,10 @@ pub mod tasks {
pub use crate::display::__display_task;

#[cfg(feature = "usb")]
pub use crate::usb::{__start_usb, __usb_hid_consumer_write_task, __usb_hid_kb_write_task};
pub use crate::usb::{
__start_usb, __usb_hid_consumer_write_task, __usb_hid_kb_write_task,
__usb_hid_mouse_write_task,
};

#[cfg(all(feature = "via", feature = "usb"))]
pub use crate::usb::__usb_hid_via_read_task;
Expand Down
Loading