From fff5a8090eba901b059e202059f842f42590f645 Mon Sep 17 00:00:00 2001 From: Haobo Gu Date: Fri, 19 Jan 2024 11:26:44 +0800 Subject: [PATCH] feat(example): add stm32f4 example Signed-off-by: Haobo Gu --- Cargo.lock | 4 +- Cargo.toml | 8 ++- boards/stm32f4/.cargo/config.toml | 36 ++++++++++ boards/stm32f4/Cargo.toml | 57 ++++++++++++++++ boards/stm32f4/build.rs | 48 +++++++++++++ boards/stm32f4/memory.x | 11 +++ boards/stm32f4/src/keymap.rs | 21 ++++++ boards/stm32f4/src/macros.rs | 32 +++++++++ boards/stm32f4/src/main.rs | 109 ++++++++++++++++++++++++++++++ 9 files changed, 323 insertions(+), 3 deletions(-) create mode 100644 boards/stm32f4/.cargo/config.toml create mode 100644 boards/stm32f4/Cargo.toml create mode 100644 boards/stm32f4/build.rs create mode 100644 boards/stm32f4/memory.x create mode 100644 boards/stm32f4/src/keymap.rs create mode 100644 boards/stm32f4/src/macros.rs create mode 100644 boards/stm32f4/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 9b633ea8..e3def7f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1448,9 +1448,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "smallvec" -version = "1.12.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2593d31f82ead8df961d8bd23a64c2ccf2eb5dd34b0a34bfb4dd54011c72009e" +checksum = "3b187f0231d56fe41bfb12034819dd2bf336422a5866de41bc3fec4b2e3883e8" [[package]] name = "ssmarshal" diff --git a/Cargo.toml b/Cargo.toml index 80e0fb11..013715ed 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,12 @@ [workspace] resolver = "2" -members = ["rmk", "boards/*"] +members = [ + "rmk", + "boards/rp2040", + # Uncomment to choose one chip you want to use + "boards/stm32h7", + #"boards/stm32f4" +] default-members = ["boards/stm32h7", "rmk"] # Profile dev: optimize for fast compiling with debug info diff --git a/boards/stm32f4/.cargo/config.toml b/boards/stm32f4/.cargo/config.toml new file mode 100644 index 00000000..cfb1fed2 --- /dev/null +++ b/boards/stm32f4/.cargo/config.toml @@ -0,0 +1,36 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# uncomment ONE of these three option to make `cargo run` start a GDB session +# which option to pick depends on your system +runner = "probe-rs run --chip STM32F411CEUx" +# runner = "gdb-multiarch -q -x openocd.gdb" +# runner = "gdb -q -x openocd.gdb" + +rustflags = [ + # Previously, the linker arguments --nmagic and -Tlink.x were set here. + # They are now set by build.rs instead. The linker argument can still + # only be set here, if a custom linker is needed. + + # By default, the LLD linker is used, which is shipped with the Rust + # toolchain. If you run into problems with LLD, you can switch to the + # GNU linker by uncommenting this line: + # "-C", "linker=arm-none-eabi-ld", + + # If you need to link to pre-compiled C libraries provided by a C toolchain + # use GCC as the linker by uncommenting the three lines below: + # "-C", "linker=arm-none-eabi-gcc", + # "-C", "link-arg=-Wl,-Tlink.x", + # "-C", "link-arg=-nostartfiles", +] + +[build] +# Pick ONE of these default compilation targets +# target = "thumbv6m-none-eabi" # Cortex-M0 and Cortex-M0+ +# target = "thumbv7m-none-eabi" # Cortex-M3 +# target = "thumbv7em-none-eabi" # Cortex-M4 and Cortex-M7 (no FPU) +target = "thumbv7em-none-eabihf" # Cortex-M4F and Cortex-M7F (with FPU) +# target = "thumbv8m.base-none-eabi" # Cortex-M23 +# target = "thumbv8m.main-none-eabi" # Cortex-M33 (no FPU) +# target = "thumbv8m.main-none-eabihf" # Cortex-M33 (with FPU) + +[env] +DEFMT_LOG = "info" \ No newline at end of file diff --git a/boards/stm32f4/Cargo.toml b/boards/stm32f4/Cargo.toml new file mode 100644 index 00000000..df2966f2 --- /dev/null +++ b/boards/stm32f4/Cargo.toml @@ -0,0 +1,57 @@ +cargo-features = ["per-package-target"] + +[package] +name = "rmk-stm32f4" +version = "0.1.0" +authors = ["Haobo Gu "] +description = "Keyboard firmware written in Rust" +homepage = "https://github.com/haobogu/rmk" +repository = "https://github.com/haobogu/rmk" +readme = "../../README.md" +edition = "2021" +license = "MIT OR Apache-2.0" + +forced-target = "thumbv7em-none-eabihf" + +[dependencies] +rmk = { version = "0.1.0", path = "../../rmk", features = [ + "eeprom", + "col2row", +] } +cortex-m = { version = "0.7.7", features = ['critical-section-single-core'] } +cortex-m-rt = "0.7.3" +embassy-time = { version = "0.3", git = "https://github.com/embassy-rs/embassy", features = [ + # "tick-hz-1_000_000", + "tick-hz-32_768", + "defmt", +] } +embassy-stm32 = { version = "0.1.0", git = "https://github.com/embassy-rs/embassy", features = [ + "stm32f411ce", + "defmt", + "time-driver-any", + "exti", + "time", +] } +embassy-executor = { version = "0.5.0", git = "https://github.com/embassy-rs/embassy", features = [ + "defmt", + "arch-cortex-m", + "executor-thread", + "executor-interrupt", + "integrated-timers", +] } +embassy-futures = { version = "0.1.0", git = "https://github.com/embassy-rs/embassy" } +rtt-target = "0.5.0" +log = "0.4.19" +packed_struct = { version = "0.10.1", default-features = false } +embedded-storage = { version = "0.3" } +static_cell = { version = "2" } + +# defmt deps +defmt = "0.3.5" +defmt-rtt = "0.4.0" +panic-probe = {version = "0.3", features = ["print-defmt"] } + +[[bin]] +name = "rmk-stm32f4" +test = false +bench = false diff --git a/boards/stm32f4/build.rs b/boards/stm32f4/build.rs new file mode 100644 index 00000000..2bfc6a14 --- /dev/null +++ b/boards/stm32f4/build.rs @@ -0,0 +1,48 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. +//! +//! The build script also sets the linker flags to tell it which link script to use. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + // Specify linker arguments. + + // `--nmagic` is required if memory section addresses are not aligned to 0x10000, + // for example the FLASH and RAM sections in your `memory.x`. + // See https://github.com/rust-embedded/cortex-m-quickstart/pull/95 + println!("cargo:rustc-link-arg=--nmagic"); + + // Set the linker script to the one provided by cortex-m-rt. + println!("cargo:rustc-link-arg=-Tlink.x"); + + // Set the extra linker script from defmt + println!("cargo:rustc-link-arg=-Tdefmt.x"); + + println!("cargo:rustc-linker=flip-link"); +} diff --git a/boards/stm32f4/memory.x b/boards/stm32f4/memory.x new file mode 100644 index 00000000..42978d9e --- /dev/null +++ b/boards/stm32f4/memory.x @@ -0,0 +1,11 @@ +MEMORY +{ + /* NOTE K = KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x08000000, LENGTH = 128K + RAM : ORIGIN = 0x20000000, LENGTH = 128K +} + +/* This is where the call stack will be allocated. */ +/* The stack is of the full descending type. */ +/* NOTE Do NOT modify `_stack_start` unless you know what you are doing */ +_stack_start = ORIGIN(RAM) + LENGTH(RAM); \ No newline at end of file diff --git a/boards/stm32f4/src/keymap.rs b/boards/stm32f4/src/keymap.rs new file mode 100644 index 00000000..c4d55227 --- /dev/null +++ b/boards/stm32f4/src/keymap.rs @@ -0,0 +1,21 @@ +use rmk::action::KeyAction; +use rmk::{a, k, layer, mo}; +pub(crate) const COL: usize = 3; +pub(crate) const ROW: usize = 4; +pub(crate) const NUM_LAYER: usize = 2; + +#[rustfmt::skip] +pub static KEYMAP: [[[KeyAction; COL]; ROW]; NUM_LAYER] = [ + layer!([ + [k!(AudioVolUp), k!(B), k!(AudioVolDown)], + [k!(Kp4), k!(LShift), k!(Kp6)], + [mo!(1), k!(Kp2), k!(Kp3)], + [mo!(1), a!(No), k!(Kp0)] + ]), + layer!([ + [k!(Kp7), k!(Kp8), k!(Kp9)], + [k!(Kp4), k!(LCtrl), k!(Kp6)], + [mo!(1), k!(Kp2), k!(Kp3)], + [mo!(1), a!(No), k!(Kp0)] + ]), +]; diff --git a/boards/stm32f4/src/macros.rs b/boards/stm32f4/src/macros.rs new file mode 100644 index 00000000..7808d3a4 --- /dev/null +++ b/boards/stm32f4/src/macros.rs @@ -0,0 +1,32 @@ +// DEPRECIATED +macro_rules! _config_matrix_pins { + (input: [$($in_port:ident.$in_pin:ident), *], output: [$($out_port:ident.$out_pin:ident), +]) => { + { + $( + let $in_pin = $in_port.$in_pin.into_pull_down_input().erase(); + )* + $( + let mut $out_pin = $out_port.$out_pin.into_push_pull_output().erase(); + )+ + $( + $out_pin.set_low(); + )+ + let output_pins = [$($out_pin), +]; + let input_pins = [$($in_pin), +]; + (input_pins, output_pins) + } + }; +} + +macro_rules! config_matrix_pins_stm32 { + (peripherals: $p:ident, input: [$($in_pin:ident), *], output: [$($out_pin:ident), +]) => { + { + let mut output_pins = [$(Output::new($p.$out_pin, embassy_stm32::gpio::Level::Low, embassy_stm32::gpio::Speed::VeryHigh).degrade()), +]; + let input_pins = [$(Input::new($p.$in_pin, embassy_stm32::gpio::Pull::Down).degrade()), +]; + output_pins.iter_mut().for_each(|p| { + p.set_low(); + }); + (input_pins, output_pins) + } + }; +} diff --git a/boards/stm32f4/src/main.rs b/boards/stm32f4/src/main.rs new file mode 100644 index 00000000..221603a4 --- /dev/null +++ b/boards/stm32f4/src/main.rs @@ -0,0 +1,109 @@ +#![no_main] +#![no_std] +#![feature(type_alias_impl_trait)] +#![allow(dead_code)] + +#[macro_use] +mod macros; +mod keymap; + +use core::{cell::RefCell, sync::atomic::AtomicBool}; +use defmt::*; +use defmt_rtt as _; +use embassy_executor::Spawner; +use embassy_futures::join::join; +use embassy_stm32::{ + bind_interrupts, + flash::{Blocking, Flash}, + gpio::{AnyPin, Input, Output}, + peripherals::USB_OTG_FS, + usb_otg::{Driver, InterruptHandler}, + Config, +}; +use embassy_time::Timer; +use panic_probe as _; +use rmk::{eeprom::EepromStorageConfig, initialize_keyboard_and_usb_device, keymap::KeyMap}; +use static_cell::StaticCell; + +use crate::keymap::{COL, NUM_LAYER, ROW}; + +bind_interrupts!(struct Irqs { + OTG_FS => InterruptHandler; +}); + +static SUSPENDED: AtomicBool = AtomicBool::new(false); +const FLASH_SECTOR_7_ADDR: u32 = 0x60000; +const EEPROM_SIZE: usize = 128; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("start!"); + // RCC config + let config = Config::default(); + + // Initialize peripherals + let p = embassy_stm32::init(config); + + // Usb config + static EP_OUT_BUFFER: StaticCell<[u8; 1024]> = StaticCell::new(); + let mut usb_config = embassy_stm32::usb_otg::Config::default(); + usb_config.vbus_detection = false; + let driver = Driver::new_fs( + p.USB_OTG_FS, + Irqs, + p.PA12, + p.PA11, + &mut EP_OUT_BUFFER.init([0; 1024])[..], + usb_config, + ); + + // Pin config + let (input_pins, output_pins) = config_matrix_pins_stm32!(peripherals: p, input: [PD9, PD8, PB13, PB12], output: [PE13, PE14, PE15]); + + // Keymap + eeprom config + static MY_KEYMAP: StaticCell< + RefCell, EEPROM_SIZE, ROW, COL, NUM_LAYER>>, + > = StaticCell::new(); + let eeprom_storage_config = EepromStorageConfig { + start_addr: FLASH_SECTOR_7_ADDR, + storage_size: 0x20000, // uses last sector, 128KB for eeprom + page_size: 8, + }; + // Use internal flash to emulate eeprom + let f = Flash::new_blocking(p.FLASH); + let keymap = MY_KEYMAP.init(RefCell::new(KeyMap::new( + crate::keymap::KEYMAP, + Some(f), + eeprom_storage_config, + None, + ))); + + // Initialize all utilities: keyboard, usb and keymap + let (mut keyboard, mut usb_device, vial) = initialize_keyboard_and_usb_device::< + Driver<'_, USB_OTG_FS>, + Input<'_, AnyPin>, + Output<'_, AnyPin>, + Flash<'_, Blocking>, + EEPROM_SIZE, + ROW, + COL, + NUM_LAYER, + >(driver, input_pins, output_pins, keymap); + + let usb_fut = usb_device.device.run(); + let keyboard_fut = async { + loop { + let _ = keyboard.keyboard_task().await; + keyboard.send_report(&mut usb_device.keyboard_hid).await; + keyboard.send_media_report(&mut usb_device.other_hid).await; + } + }; + + let via_fut = async { + loop { + vial.process_via_report(&mut usb_device.via_hid).await; + Timer::after_millis(1).await; + } + }; + join(usb_fut, join(keyboard_fut, via_fut)).await; +}