Skip to content

Commit

Permalink
rp2040 support
Browse files Browse the repository at this point in the history
  • Loading branch information
Univa committed Mar 13, 2024
1 parent cf0a84c commit 30bdb70
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 16 deletions.
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,7 @@ and flashing instructions for some common setups.
- STM32F072CBx
- STM32F303CBx
- nRF52840 (tested with nice!nano v2)

### Planned MCUs for the future

- RP-based chips (I don't have access to an RP-based keyboard at the moment)
- RP2040

## Features

Expand Down
33 changes: 30 additions & 3 deletions docs/src/content/docs/features/feature-storage.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,25 +69,52 @@ __config_end = __config_start + LENGTH(CONFIG); /* add this */
Finally, you can add `storage(driver = "internal")` to your `#[keyboard]` macro invocation, and make sure to implement
`StorageDevice` for your keyboard:

```rust ins={5,7,11-12}
```rust ins={5,7-11,16-23}
#[keyboard(
// somewhere in your keyboard macro invocation ...
underglow(
driver = "ws2812_bitbang",
use_storage // This underglow feature uses storage
),
storage(driver = "internal")
storage(
driver = "internal",
// `flash_size` below is required for RP2040, omit if you are not using an RP2040.
// Should be equal to the total size of the flash chip (not the size of your CONFIG partition)
flash_size = 2097152
)
)]
struct MyKeyboard;

use rumcake::storage::StorageDevice;
impl StorageDevice for MyKeyboard {}

// Required for RP2040, omit if you are not using an RP2040
use rumcake::hw::mcu::setup_dma_channel;
impl RP2040FlashSettings for MyKeyboard {
setup_dma_channel! { DMA_CH0 }
}
```

:::note
**For RP2040 users**: You must implement the `RP2040FlashSettings` trait (generated by the
`#[keyboard]` macro), and the `#[keyboard]` macro invocation must also include `flash_size` as
shown in the example above. If you are not using RP2040, these things can be omitted.
:::

:::tip
By default, the `setup_storage_buffer()` function in the `StorageDevice` trait creates a buffer
with a size of 1024 bytes. You can override the implementation to increase the size of the
buffer to store values that may be larger, or you can decrease the size to save memory.
buffer to store values that may be larger, or you can decrease the size to save memory:

```rust del={3} ins={4}
impl StorageDevice for MyKeyboard {
fn get_storage_buffer() -> &'static mut [u8] {
static mut STORAGE_BUFFER: [u8; 1024] = [0; 1024];
static mut STORAGE_BUFFER: [u8; 32] = [0; 32];
unsafe { &mut STORAGE_BUFFER }
}
}
```

Keep in mind, that the size of this buffer must be large enough to store the largest possible value
that you will be reading, or writing from the storage peripheral.
Expand Down
5 changes: 1 addition & 4 deletions docs/src/content/docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,7 @@ and flashing instructions for some common setups.
- STM32F072CBx
- STM32F303CBx
- nRF52840 (tested with nice!nano v2)

## Planned MCUs for the future

- RP-based chips (I don't have access to an RP-based keyboard at the moment)
- RP2040

# Features

Expand Down
1 change: 1 addition & 0 deletions rumcake-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ embassy-stm32 = { git = "https://github.com/embassy-rs/embassy", features = ["de
[features]
stm32 = []
nrf = []
rp = []

storage = []

Expand Down
137 changes: 137 additions & 0 deletions rumcake-macros/src/hw/rp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use proc_macro2::{Ident, TokenStream};
use proc_macro_error::OptionExt;
use quote::quote;
use syn::punctuated::Punctuated;
use syn::Token;

pub fn input_pin(ident: Ident) -> TokenStream {
quote! {
unsafe {
::rumcake::hw::mcu::embassy_rp::gpio::Input::new(
::rumcake::hw::mcu::embassy_rp::gpio::Pin::degrade(
::rumcake::hw::mcu::embassy_rp::peripherals::#ident::steal(),
),
::rumcake::hw::mcu::embassy_rp::gpio::Pull::Up,
)
}
}
}

pub fn output_pin(ident: Ident) -> TokenStream {
quote! {
unsafe {
::rumcake::hw::mcu::embassy_rp::gpio::Output::new(
::rumcake::hw::mcu::embassy_rp::gpio::Pin::degrade(
::rumcake::hw::mcu::embassy_rp::peripherals::#ident::steal(),
),
::rumcake::hw::mcu::embassy_rp::gpio::Level::High,
)
}
}
}

pub fn internal_storage_trait() -> TokenStream {
quote! {
/// A trait that must be implemented to use the flash chip connected to your RP2040 for storage..
pub(crate) trait RP2040FlashSettings {
/// Get the DMA channel used for flash operations.
fn setup_dma_channel(
) -> impl ::rumcake::hw::mcu::embassy_rp::Peripheral<P = impl ::rumcake::hw::mcu::embassy_rp::dma::Channel>;
}
}
}

pub fn setup_dma_channel(dma: Ident) -> TokenStream {
quote! {
fn setup_dma_channel(
) -> impl ::rumcake::hw::mcu::embassy_rp::Peripheral<P = impl ::rumcake::hw::mcu::embassy_rp::dma::Channel> {
unsafe {
::rumcake::hw::mcu::embassy_rp::peripherals::#dma::steal()
}
}
}
}

fn setup_i2c_inner(args: Punctuated<Ident, Token![,]>) -> TokenStream {
let mut args = args.iter();

let interrupt = args.next().expect_or_abort("Missing interrupt argument.");
let i2c = args
.next()
.expect_or_abort("Missing I2C peripheral argument.");
let scl = args
.next()
.expect_or_abort("Missing SCL peripheral argument.");
let sda = args
.next()
.expect_or_abort("Missing SDA peripheral argument.");

quote! {
unsafe {
::rumcake::hw::mcu::embassy_rp::bind_interrupts! {
struct Irqs {
#interrupt => ::rumcake::hw::mcu::embassy_rp::i2c::InterruptHandler<::rumcake::hw::mcu::embassy_rp::peripherals::#i2c>;
}
};
let i2c = ::rumcake::hw::mcu::embassy_rp::peripherals::#i2c::steal();
let scl = ::rumcake::hw::mcu::embassy_rp::peripherals::#scl::steal();
let sda = ::rumcake::hw::mcu::embassy_rp::peripherals::#sda::steal();
::rumcake::hw::mcu::embassy_rp::i2c::I2c::new_async(i2c, scl, sda, Irqs, Default::default())
}
}
}

pub fn setup_i2c(args: Punctuated<Ident, Token![,]>) -> TokenStream {
let inner = setup_i2c_inner(args);
quote! {
fn setup_i2c() -> impl ::rumcake::embedded_hal_async::i2c::I2c<Error = impl core::fmt::Debug> {
#inner
}
}
}

fn setup_buffered_uart_inner(args: Punctuated<Ident, Token![,]>) -> TokenStream {
let mut args = args.iter();

let interrupt = args.next().expect_or_abort("Missing interrupt argument.");
let uart = args
.next()
.expect_or_abort("Missing UART peripheral argument.");
let rx_pin = args.next().expect_or_abort("Missing RX pin argument.");
let tx_pin = args.next().expect_or_abort("Missing TX pin argument.");

quote! {
unsafe {
static mut RBUF: [u8; 64] = [0; 64];
static mut TBUF: [u8; 64] = [0; 64];
::rumcake::hw::mcu::embassy_rp::bind_interrupts! {
struct Irqs {
#interrupt => ::rumcake::hw::mcu::embassy_rp::uart::BufferedInterruptHandler<::rumcake::hw::mcu::embassy_rp::peripherals::#uart>;
}
};
let uart = ::rumcake::hw::mcu::embassy_rp::peripherals::#uart::steal();
let rx = ::rumcake::hw::mcu::embassy_rp::peripherals::#rx_pin::steal();
let tx = ::rumcake::hw::mcu::embassy_rp::peripherals::#tx_pin::steal();
::rumcake::hw::mcu::embassy_rp::uart::BufferedUart::new(
uart,
Irqs,
rx,
tx,
&mut TBUF,
&mut RBUF,
Default::default(),
)
}
}
}

pub fn setup_buffered_uart(args: Punctuated<Ident, Token![,]>) -> TokenStream {
let inner = setup_buffered_uart_inner(args);

quote! {
fn setup_serial(
) -> impl ::rumcake::embedded_io_async::Write + ::rumcake::embedded_io_async::Read {
#inner
}
}
}
37 changes: 35 additions & 2 deletions rumcake-macros/src/keyboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub(crate) struct ViaSettings {
#[darling(default)]
pub(crate) struct StorageSettings {
driver: String,
flash_size: usize,
}

enum SplitSettings<'a> {
Expand Down Expand Up @@ -256,6 +257,8 @@ fn setup_display_driver(

fn setup_storage_driver(
initialization: &mut TokenStream,
traits: &mut HashMap<String, TokenStream>,
kb_name: &Ident,
config: &StorageSettings,
uses_bluetooth: bool,
) {
Expand All @@ -273,7 +276,7 @@ fn setup_storage_driver(
static DATABASE: ::rumcake::storage::StorageService<'static, ::rumcake::hw::mcu::nrf_softdevice::Flash> = ::rumcake::storage::StorageService::new();
unsafe { DATABASE.setup(flash, config_start, config_end, &mut READ_BUF, &mut OP_BUF).await; }
})
} else {
} else if cfg!(any(feature = "stm32", feature = "nrf")) {
initialization.extend(quote! {
use ::rumcake::storage::FlashStorage;
let flash = ::rumcake::hw::mcu::setup_internal_flash();
Expand All @@ -284,6 +287,30 @@ fn setup_storage_driver(
static DATABASE: ::rumcake::storage::StorageService<'static, ::rumcake::hw::mcu::Flash> = ::rumcake::storage::StorageService::new();
unsafe { DATABASE.setup(flash, config_start, config_end, &mut READ_BUF, &mut OP_BUF).await; }
})
} else if cfg!(feature = "rp") {
#[cfg(feature = "rp")]
traits.insert(config.driver.clone(), crate::hw::internal_storage_trait());
if config.flash_size == 0 {
initialization.extend(quote_spanned! {
config.driver.span() => compile_error!("You must specify a non-zero size for your flash chip.");
});
} else {
let size = config.flash_size;
initialization.extend(quote! {
use ::rumcake::storage::FlashStorage;
let flash = ::rumcake::hw::mcu::setup_internal_flash::<#size>(<#kb_name as RP2040FlashSettings>::setup_dma_channel());
let config_start = unsafe { &::rumcake::hw::__config_start as *const u32 as usize };
let config_end = unsafe { &::rumcake::hw::__config_end as *const u32 as usize };
static mut READ_BUF: [u8; ::rumcake::hw::mcu::embassy_rp::flash::ERASE_SIZE] = [0; ::rumcake::hw::mcu::embassy_rp::flash::ERASE_SIZE];
static mut OP_BUF: [u8; ::rumcake::hw::mcu::embassy_rp::flash::ERASE_SIZE] = [0; ::rumcake::hw::mcu::embassy_rp::flash::ERASE_SIZE];
static DATABASE: ::rumcake::storage::StorageService<'static, ::rumcake::hw::mcu::Flash<#size>> = ::rumcake::storage::StorageService::new();
unsafe { DATABASE.setup(flash, config_start, config_end, &mut READ_BUF, &mut OP_BUF).await; }
})
}
} else {
initialization.extend(quote_spanned! {
config.driver.span() => compile_error!("Internal storage driver is not available for your platform.");
});
};
}
_ => (),
Expand Down Expand Up @@ -354,7 +381,13 @@ pub(crate) fn keyboard_main(
driver.driver.span() => compile_error!("Storage driver was specified, but rumcake's `storage` feature flag is not enabled. Please enable the feature.");
});
} else {
setup_storage_driver(&mut initialization, driver, uses_bluetooth);
setup_storage_driver(
&mut initialization,
&mut traits,
&kb_name,
driver,
uses_bluetooth,
);
}
};

Expand Down
10 changes: 9 additions & 1 deletion rumcake-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ pub fn is31fl3731_get_led_from_rgb_matrix_coordinates(

#[cfg_attr(feature = "stm32", path = "hw/stm32.rs")]
#[cfg_attr(feature = "nrf", path = "hw/nrf.rs")]
#[cfg_attr(feature = "rp", path = "hw/rp.rs")]
mod hw;

#[proc_macro]
Expand Down Expand Up @@ -244,13 +245,20 @@ pub fn setup_buffered_uarte(input: proc_macro::TokenStream) -> proc_macro::Token
hw::setup_buffered_uarte(ident).into()
}

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

#[cfg(feature = "rp")]
#[proc_macro]
pub fn setup_dma_channel(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let args = parse_macro_input!(input as Ident);
hw::setup_dma_channel(args).into()
}

mod via;

#[proc_macro]
Expand Down
6 changes: 6 additions & 0 deletions rumcake/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ features = [

flavours = [
{ feature = "nrf52840", triple = "thumbv7em-none-eabihf", extra_features = ["nrf-ble", "bluetooth"] },
{ feature = "rp2040", triple = "thumbv6m-none-eabi", extra_features = [] },
{ feature = "stm32f072cb", triple = "thumbv6m-none-eabi", extra_features = [] },
{ feature = "stm32f303cb", triple = "thumbv7em-none-eabihf", extra_features = [] },
]
Expand All @@ -45,6 +46,7 @@ embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "b8be12
embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "b8be126", features = ["defmt", "integrated-timers", "executor-thread"] }
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-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 }
Expand Down Expand Up @@ -86,6 +88,10 @@ drivers = []
# Chips
#

# RP
rp = ["dep:cortex-m", "embassy-executor/arch-cortex-m", "dep:embassy-rp", "rumcake-macros/rp"]
rp2040 = ["rp", "embassy-rp/time-driver"]

# STM32
stm32 = ["dep:cortex-m", "embassy-executor/arch-cortex-m", "dep:embassy-stm32", "rumcake-macros/stm32"]
stm32f072cb = ["stm32", "embassy-stm32/stm32f072cb", "embassy-stm32/time-driver-any"]
Expand Down
Loading

0 comments on commit 30bdb70

Please sign in to comment.