Skip to content

Commit

Permalink
merge encoders:
Browse files Browse the repository at this point in the history
commit 6aa935cf17a3e1d76a7db934c1c04286948e31b8
Author: Univa <41708691+Univa@users.noreply.github.com>
Date:   Fri Apr 5 18:30:29 2024 -0400

    add missing proc_macro_error attributes

commit 834cf540470be33a51c9ca45b16a487174a272e7
Author: Univa <41708691+Univa@users.noreply.github.com>
Date:   Fri Apr 5 18:28:13 2024 -0400

    encoder support
  • Loading branch information
Univa committed Apr 6, 2024
1 parent c52a9c7 commit 1037dbf
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 15 deletions.
95 changes: 95 additions & 0 deletions docs/src/content/docs/features/feature-encoders.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: Encoders
description: How to add EC11-compatible encoders to your device.
---

:::caution
This feature is still a work in progress. For a list of features that still need
to be implemented, check the [to-do list](#to-do-list).
:::

This document contains information about how to add EC11-compatible encoders to your device.

# Setup

## Required code

To set up your keyboard for use with encoders, you must add `encoders` to your `#[keyboard]` macro invocation,
and your keyboard must implement the `DeviceWithEncoders` trait.

This can be done easily by using the `setup_encoders!` macro:

```rust ins={5,9-31}
use rumcake::keyboard;

#[keyboard(
// somewhere in your keyboard macro invocation ...
encoders
)]
struct MyKeyboard;

use rumcake::keyboard::DeviceWithEncoders;
impl DeviceWithEncoders for MyKeyboard {
type Layout = Self;

setup_encoders! {
Encoder {
sw_pin: input_pin!(PB12, EXTI12),
sw_pos: (0, 0),
output_a_pin: input_pin!(PB2, EXTI2),
output_b_pin: input_pin!(PB1),
cw_pos: (0, 1),
ccw_pos: (0, 2),
},
Encoder {
sw_pin: input_pin!(PA11, EXTI11),
sw_pos: (1, 0),
output_a_pin: input_pin!(PA3, EXTI3),
output_b_pin: input_pin!(PA1),
cw_pos: (1, 1),
ccw_pos: (1, 2),
},
};
}

use rumcake::keyboard::{build_layout, KeyboardLayout};
impl KeyboardLayout for MyKeyboard {
build_layout! {
{
[ A B C ]
[ D E F ]
}
{
[ G H I ]
[ J K L ]
}
}
}
```

The `sw_pin` corresponds to the pin connected to the encoder's push button. `output_a_pin` and `output_b_pin`
correspond to the pins that pulse as the encoder rotates.

:::note
The current implementation of encoders relies on interrupts to avoid polling the encoders constantly.

For STM32, this means you need to specify the EXTI channels for `sw_pin` and `output_a_pin`. This can
be done by adding an extra argument to the `input_pin!` macro, as shown in the example above. This can
be omitted for other platforms.
:::

Encoders work by mapping their outputs to a position on your layout.
`type Layout = Self` tells rumcake to redirect encoder events to the implemented `KeyboardLayout` for `MyKeyboard`.

In the example above, here are the following mappings:

- Encoder 1 Button: `A` key (or `G` on the second layer)
- Encoder 1 Clockwise rotation: `B` key (or `H` on the second layer)
- Encoder 1 Counter-clockwise rotation: `C` key (or `I` on the second layer)
- Encoder 2 Button: `D` key (or `J` on the second layer)
- Encoder 2 Clockwise rotation: `H` key (or `K` on the second layer)
- Encoder 2 Counter-clockwise rotation: `I` key (or `L` on the second layer)

# To-do List

- [ ] Via(l) support
36 changes: 28 additions & 8 deletions rumcake-macros/src/hw/stm32.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,35 @@ use crate::common::{

pub const HAL_CRATE: &str = "embassy_stm32";

pub fn input_pin(ident: Ident) -> TokenStream {
quote! {
unsafe {
::rumcake::hw::platform::embassy_stm32::gpio::Input::new(
::rumcake::hw::platform::embassy_stm32::gpio::Pin::degrade(
pub fn input_pin(args: Punctuated<Ident, Token![,]>) -> TokenStream {
let mut args = args.iter();
let ident = args.next().expect_or_abort("missing pin identifier");
let exti_channel = args.next();

if let Some(arg) = args.next() {
abort!(arg, "unexpected extra args provided");
}

if let Some(exti) = exti_channel {
quote! {
unsafe {
::rumcake::hw::platform::embassy_stm32::exti::ExtiInput::new(
::rumcake::hw::platform::embassy_stm32::peripherals::#ident::steal(),
),
::rumcake::hw::platform::embassy_stm32::gpio::Pull::Up,
)
::rumcake::hw::platform::embassy_stm32::peripherals::#exti::steal(),
::rumcake::hw::platform::embassy_stm32::gpio::Pull::Up,
)
}
}
} else {
quote! {
unsafe {
::rumcake::hw::platform::embassy_stm32::gpio::Input::new(
::rumcake::hw::platform::embassy_stm32::gpio::Pin::degrade(
::rumcake::hw::platform::embassy_stm32::peripherals::#ident::steal(),
),
::rumcake::hw::platform::embassy_stm32::gpio::Pull::Up,
)
}
}
}
}
Expand Down
84 changes: 83 additions & 1 deletion rumcake-macros/src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ use darling::FromMeta;
use proc_macro2::{Ident, TokenStream, TokenTree};
use proc_macro_error::{abort, emit_error, OptionExt};
use quote::quote;
use syn::{ExprRange, ItemStruct, LitInt, LitStr, PathSegment};
use syn::parse::Parse;
use syn::punctuated::Punctuated;
use syn::{
braced, custom_keyword, Expr, ExprRange, ItemStruct, LitInt, LitStr, PathSegment, Token,
};

use crate::common::{Layer, LayoutLike, MatrixLike, OptionalItem, Row};
use crate::TuplePair;
Expand All @@ -14,6 +18,7 @@ pub(crate) struct KeyboardSettings {
no_matrix: bool,
bluetooth: bool,
usb: bool,
encoders: bool,
storage: Option<StorageSettings>,
simple_backlight: Option<LightingSettings>,
simple_backlight_matrix: Option<LightingSettings>,
Expand Down Expand Up @@ -291,6 +296,14 @@ pub(crate) fn keyboard_main(
});
}

if keyboard.encoders {
spawning.extend(quote! {
spawner
.spawn(::rumcake::ec11_encoders_poll!(#kb_name))
.unwrap();
})
}

// Flash setup
if let Some(ref driver) = keyboard.storage {
if !cfg!(feature = "storage") {
Expand Down Expand Up @@ -876,6 +889,75 @@ pub fn build_layout(raw: TokenStream, layers: LayoutLike<TokenTree>) -> TokenStr
}
}

crate::parse_as_custom_fields! {
pub struct SetupEncoderArgsBuilder for SetupEncoderArgs {
sw_pin: Expr,
output_a_pin: Expr,
output_b_pin: Expr,
sw_pos: TuplePair,
cw_pos: TuplePair,
ccw_pos: TuplePair,
}
}

custom_keyword!(Encoder);

pub struct EncoderDefinition {
encoder_keyword: Encoder,
brace_token: syn::token::Brace,
encoder_args: SetupEncoderArgs,
}

impl Parse for EncoderDefinition {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
let content;
Ok(Self {
encoder_keyword: input.parse()?,
brace_token: braced!(content in input),
encoder_args: content.parse()?,
})
}
}

pub fn setup_encoders(encoders: Punctuated<EncoderDefinition, Token![,]>) -> TokenStream {
let count = encoders.len();

let (positions, definitions): (Vec<TokenStream>, Vec<TokenStream>) = encoders
.iter()
.map(|EncoderDefinition { encoder_args, .. }| {
let SetupEncoderArgs {
sw_pin,
output_a_pin,
output_b_pin,
sw_pos,
cw_pos,
ccw_pos,
} = encoder_args;

(
quote! {
[#sw_pos, #cw_pos, #ccw_pos]
},
quote! {
::rumcake::keyboard::EC11Encoder::new(#sw_pin, #output_a_pin, #output_b_pin)
},
)
})
.unzip();

quote! {
const ENCODER_COUNT: usize = #count;

fn get_encoders() -> [impl ::rumcake::keyboard::Encoder; Self::ENCODER_COUNT] {
[#(#definitions),*]
}

fn get_layout_mappings() -> [[(u8, u8); 3]; Self::ENCODER_COUNT] {
[#(#positions),*]
}
}
}

crate::parse_as_custom_fields! {
pub struct RemapMacroInputBuilder for RemapMacroInput {
pub original: Layer<OptionalItem<Ident>>,
Expand Down
14 changes: 12 additions & 2 deletions rumcake-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ pub fn keyboard_main(
}

#[proc_macro]
#[proc_macro_error]
pub fn build_standard_matrix(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let matrix = parse_macro_input!(input as keyboard::StandardMatrixDefinition);
keyboard::build_standard_matrix(matrix).into()
Expand Down Expand Up @@ -190,6 +191,14 @@ pub fn build_layout(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
}

#[proc_macro]
#[proc_macro_error]
pub fn setup_encoders(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let args = parse_macro_input!(input with Punctuated<keyboard::EncoderDefinition, Token![,]>::parse_terminated);
keyboard::setup_encoders(args).into()
}

#[proc_macro]
#[proc_macro_error]
pub fn remap_matrix(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let remap = parse_macro_input!(input as keyboard::RemapMacroInput);
keyboard::remap_matrix(remap).into()
Expand Down Expand Up @@ -286,9 +295,10 @@ mod hw;

#[cfg(feature = "stm32")]
#[proc_macro]
#[proc_macro_error]
pub fn stm32_input_pin(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let ident = parse_macro_input!(input as Ident);
hw::input_pin(ident).into()
let args = parse_macro_input!(input with Punctuated<Ident, Token![,]>::parse_terminated);
hw::input_pin(args).into()
}

#[cfg(feature = "stm32")]
Expand Down
2 changes: 1 addition & 1 deletion rumcake/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "b8be1
embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "b8be126", features = ["defmt", "defmt-timestamp-uptime"] }
embassy-usb = { git = "https://github.com/embassy-rs/embassy", rev = "b8be126", features = ["defmt"] }
embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "b8be126", features = ["defmt", "unstable-pac"], optional = true }
embassy-stm32 = { git = "https://github.com/embassy-rs/embassy", rev = "b8be126", features = ["defmt", "unstable-pac"], optional = true }
embassy-stm32 = { git = "https://github.com/embassy-rs/embassy", rev = "b8be126", features = ["defmt", "unstable-pac", "exti"], optional = true }
embassy-nrf = { git = "https://github.com/embassy-rs/embassy", rev = "b8be126", features = ["defmt", "nfc-pins-as-gpio", "time-driver-rtc1"], optional = true }
nrf-softdevice = { git = "https://github.com/embassy-rs/nrf-softdevice", rev = "487f98e", optional = true }
tickv = { git = "https://github.com/tock/tock", rev = "18cf287" }
Expand Down
Loading

0 comments on commit 1037dbf

Please sign in to comment.