From 9854cde7eb8d3cdf241abde34b53084e767f4e6d Mon Sep 17 00:00:00 2001 From: Jack Rubacha Date: Tue, 12 May 2026 23:27:13 -0400 Subject: [PATCH 1/3] fuck ton of cargo fuckery --- .cargo/config.toml | 18 +- .vscode/settings.json | 8 +- .zed/settings.json | 12 + Cargo.toml | 4 +- README.md | 54 ++-- cerberus/src/dti.rs | 40 --- cerberus/src/main.rs | 234 ---------------- crates/{ => drivers}/lsm6dso-ner/Cargo.toml | 0 crates/{ => drivers}/lsm6dso-ner/src/lib.rs | 0 crates/{ => drivers}/lsm6dso-ner/src/regs.rs | 0 crates/{ => drivers}/pca9539-ner/Cargo.toml | 0 crates/{ => drivers}/pca9539-ner/src/lib.rs | 0 crates/{ => drivers}/sht3x-ner/Cargo.toml | 0 crates/{ => drivers}/sht3x-ner/src/lib.rs | 0 crates/{ => drivers}/vl6180x-ner/Cargo.toml | 0 .../{ => drivers}/vl6180x-ner/src/config.rs | 0 .../vl6180x-ner/src/device_status.rs | 0 crates/{ => drivers}/vl6180x-ner/src/error.rs | 0 .../vl6180x-ner/src/i2c_interface.rs | 0 crates/{ => drivers}/vl6180x-ner/src/init.rs | 0 crates/{ => drivers}/vl6180x-ner/src/lib.rs | 0 crates/{ => drivers}/vl6180x-ner/src/mode.rs | 0 .../vl6180x-ner/src/mode/continuous.rs | 0 .../vl6180x-ner/src/mode/dynamic.rs | 0 .../vl6180x-ner/src/mode/powered_off.rs | 0 .../vl6180x-ner/src/mode/ready.rs | 0 .../vl6180x-ner/src/read_measurements.rs | 0 .../{ => drivers}/vl6180x-ner/src/register.rs | 0 .../src/start_stop_measurements.rs | 0 projects/README.md | 5 + projects/cerberus/.cargo/config.toml | 2 + {cerberus => projects/cerberus}/Cargo.toml | 7 +- {cerberus => projects/cerberus}/src/bms.rs | 0 .../cerberus}/src/can_handler.rs | 0 projects/cerberus/src/dti.rs | 37 +++ {cerberus => projects/cerberus}/src/fault.rs | 0 {cerberus => projects/cerberus}/src/lib.rs | 4 +- projects/cerberus/src/main.rs | 262 ++++++++++++++++++ .../cerberus}/src/monitor.rs | 50 ++-- .../cerberus}/src/state_machine.rs | 4 +- projects/lightning/.cargo/config.toml | 2 + projects/lightning/Cargo.toml | 22 ++ projects/lightning/build.rs | 5 + projects/lightning/src/main.rs | 58 ++++ projects/msb-fw-rs/.cargo/config.toml | 2 + {msb-fw-rs => projects/msb-fw-rs}/Cargo.toml | 11 +- {msb-fw-rs => projects/msb-fw-rs}/build.rs | 0 .../msb-fw-rs}/src/can_handler.rs | 0 .../msb-fw-rs}/src/controllers.rs | 0 {msb-fw-rs => projects/msb-fw-rs}/src/lib.rs | 0 {msb-fw-rs => projects/msb-fw-rs}/src/main.rs | 0 .../msb-fw-rs}/src/readers.rs | 0 projects/wheel/.cargo/config.toml | 2 + {wheel => projects/wheel}/Cargo.toml | 5 +- projects/wheel/build.rs | 6 + {wheel => projects/wheel}/src/main.rs | 35 ++- rust-toolchain.toml | 6 +- 57 files changed, 525 insertions(+), 370 deletions(-) create mode 100644 .zed/settings.json delete mode 100644 cerberus/src/dti.rs delete mode 100644 cerberus/src/main.rs rename crates/{ => drivers}/lsm6dso-ner/Cargo.toml (100%) rename crates/{ => drivers}/lsm6dso-ner/src/lib.rs (100%) rename crates/{ => drivers}/lsm6dso-ner/src/regs.rs (100%) rename crates/{ => drivers}/pca9539-ner/Cargo.toml (100%) rename crates/{ => drivers}/pca9539-ner/src/lib.rs (100%) rename crates/{ => drivers}/sht3x-ner/Cargo.toml (100%) rename crates/{ => drivers}/sht3x-ner/src/lib.rs (100%) rename crates/{ => drivers}/vl6180x-ner/Cargo.toml (100%) rename crates/{ => drivers}/vl6180x-ner/src/config.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/device_status.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/error.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/i2c_interface.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/init.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/lib.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/mode.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/mode/continuous.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/mode/dynamic.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/mode/powered_off.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/mode/ready.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/read_measurements.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/register.rs (100%) rename crates/{ => drivers}/vl6180x-ner/src/start_stop_measurements.rs (100%) create mode 100644 projects/README.md create mode 100644 projects/cerberus/.cargo/config.toml rename {cerberus => projects/cerberus}/Cargo.toml (67%) rename {cerberus => projects/cerberus}/src/bms.rs (100%) rename {cerberus => projects/cerberus}/src/can_handler.rs (100%) create mode 100644 projects/cerberus/src/dti.rs rename {cerberus => projects/cerberus}/src/fault.rs (100%) rename {cerberus => projects/cerberus}/src/lib.rs (91%) create mode 100644 projects/cerberus/src/main.rs rename {cerberus => projects/cerberus}/src/monitor.rs (89%) rename {cerberus => projects/cerberus}/src/state_machine.rs (96%) create mode 100644 projects/lightning/.cargo/config.toml create mode 100644 projects/lightning/Cargo.toml create mode 100644 projects/lightning/build.rs create mode 100644 projects/lightning/src/main.rs create mode 100644 projects/msb-fw-rs/.cargo/config.toml rename {msb-fw-rs => projects/msb-fw-rs}/Cargo.toml (56%) rename {msb-fw-rs => projects/msb-fw-rs}/build.rs (100%) rename {msb-fw-rs => projects/msb-fw-rs}/src/can_handler.rs (100%) rename {msb-fw-rs => projects/msb-fw-rs}/src/controllers.rs (100%) rename {msb-fw-rs => projects/msb-fw-rs}/src/lib.rs (100%) rename {msb-fw-rs => projects/msb-fw-rs}/src/main.rs (100%) rename {msb-fw-rs => projects/msb-fw-rs}/src/readers.rs (100%) create mode 100644 projects/wheel/.cargo/config.toml rename {wheel => projects/wheel}/Cargo.toml (74%) create mode 100644 projects/wheel/build.rs rename {wheel => projects/wheel}/src/main.rs (80%) diff --git a/.cargo/config.toml b/.cargo/config.toml index f330c4e..415d916 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,12 +1,16 @@ -[target.'cfg(all(target_arch = "arm", target_os = "none"))'] -runner = 'probe-rs run --chip STM32F405RGTx' +[target.thumbv8m.main-none-eabihf] +runner = 'probe-rs run --chip STM32H563ZITx' -[build] -target = "thumbv7em-none-eabi" +[target.thumbv7em-none-eabi] +runner = 'probe-rs run --chip STM32F405RGTx' [env] -DEFMT_LOG = "info" +DEFMT_LOG = "warn" + +# these are all so the workspace can work cohesively, and should not affect code stability + +[resolver] +feature-unification = "package" [unstable] -build-std-features = ["panic_immediate_abort"] -build-std = ["core"] \ No newline at end of file +feature-unification = true # for having a variety of features, ex stm32-metapac we cannot enable two different stm32's diff --git a/.vscode/settings.json b/.vscode/settings.json index bbaa17e..9a12958 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,4 @@ { - "rust-analyzer.checkOnSave.allTargets": false, - "rust-analyzer.diagnostics.disabled": [ - "trait-impl-incorrect-safety" - ] -} \ No newline at end of file + "rust-analyzer.cargo.target": "thumbv8m.main-none-eabihf", + "rust-analyzer.checkOnSave.allTargets": false, +} diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 0000000..d779eb0 --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,12 @@ +{ + "lsp": { + "rust-analyzer": { + "initialization_options": { + "cargo": { + "target": "thumbv8m.main-none-eabihf", + "allTargets": false + } + } + } + } +} diff --git a/Cargo.toml b/Cargo.toml index ede142e..2833f89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [workspace] -members = ["msb-fw-rs","crates/*"] +members = ["projects/*","crates/drivers/*"] resolver = "3" [workspace.dependencies] -embassy-stm32 = { version = "0.6.0", features = ["defmt", "stm32f405rg", "unstable-pac", "memory-x", "time", "time-driver-any", "exti"] } +embassy-stm32 = { version = "0.6.0", features = ["defmt", "unstable-pac", "memory-x", "time", "time-driver-any", "exti"] } embassy-sync = { version = "0.8.0", features = ["defmt"] } embassy-executor = { version = "0.10.0", features = ["executor-thread", "executor-interrupt", "defmt", "embassy-time-driver", "platform-cortex-m"] } embassy-time = { version = "0.5.1", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } diff --git a/README.md b/README.md index eb8c703..360d431 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,23 @@ -# MSB-FW.rs -Mechanical Sensor Board Firmware in Rust (experimental, not for usage on car) +# firmware-rs +NER Firmware in Rust (experimental, not for usage on car) -### Commands +## Setup -First, [get rustup](https://www.rust-lang.org/learn/get-started). +1. [get rustup](https://www.rust-lang.org/learn/get-started). +2. clone and open this root folder. +3. [get probe-rs](https://probe.rs/docs/getting-started/installation/). -Second, clone and open this root folder. - -To build `msb-fw-rs`, just run `cargo build --release`. Note the first time you do this it will take a while, as `rustup` has to install the correct version of rust for this project. - -To deploy onto an embedded chip locally connected, run `cargo run --release`. - -To build a different project other than `msb-fw-rs` (of which none exist), cd into that directory and run build. - -To format, run `cargo format`. - -To lint and check stuff, run `cargo clippy`. - -### On car stuff - -To run a RTT terminal dedicated: -`cargo embed --release rtt` - -To run a GDB terminal dedicated: -`cargo embed --release gdb` - -To flash and leave code: -`cargo embed --release` +## Commands +- To enter a project: `cd ./projects/project-name` +- To build: `cargo build` +- To deploy onto an embedded chip locally connected, run `cargo run --release`. +- To format, run `cargo format`. +- To lint and check stuff, run `cargo clippy`. +- To run a RTT terminal dedicated: `cargo embed --release rtt` +- To run a GDB terminal dedicated: `cargo embed --release gdb` +- To flash and leave code: `cargo embed --release` ### Coding tips and tricks @@ -36,19 +25,22 @@ To flash and leave code: - Use defmt macros to print stuff - +### IDE Stuff + +There are currently custom rust-analyzer settings for VSCode and zed. Feel free to adapt them to your own liking. -### Repository structure +## Repository structure -This is a mega-repo configured as a normal embassy-styled project with multiple dependency crates and sub-projects. +This is a mono-repo configured as a normal embassy-styled project with multiple dependency crates and sub-projects. Various files: -Top level `Cargo.toml`, `Embed.toml`, and `rust-toolchain.toml` define the various parts of the embassy project. See comments inside for how these were structure, but most follow embassy specification. +Top level `Cargo.toml` and `rust-toolchain.toml` define the various parts of the embassy project. See comments inside for how these were structure, but most follow embassy specification. The `crates` folder defines drivers or other code shared between projects. -Top level folders like `msb-fw-rs`, and any other project, define projects which inherit explicity defined `Cargo.toml` dependencies and `Embed.toml` settings, and more. They can also depend on a crate in the `crates` folder. +The `projects` folder defines individual board-specific compilation units which inherit explicity defined `Cargo.toml` dependencies and `Embed.toml` settings, and more. They can also depend on a crate in the `crates` folder. Notably, they all may have individual `.config/cargo.toml` if they override anything. This structure has multiple benefits, including: - Static versioning of all embassy and other dependencies, eliminating version conflicts for in-tree code @@ -57,7 +49,7 @@ This structure has multiple benefits, including: - Other quirks, such as vscode `settings.json` and `config.toml`, are shared between projects -### Versioning +### Upgrades of Embassy/Dep versions Updating versions is as follows diff --git a/cerberus/src/dti.rs b/cerberus/src/dti.rs deleted file mode 100644 index 1f50d06..0000000 --- a/cerberus/src/dti.rs +++ /dev/null @@ -1,40 +0,0 @@ -use core::{f32::consts::PI, sync::atomic::AtomicI32}; - -use embassy_stm32::can::Frame; -use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal}; - -use crate::can_handler::DTI_RPM_MSG_ID; - -const TIRE_DIAMETER: f32 = 16.0; -const GEAR_RATIO: f32 = 47.0 / 13.0; -const POLE_PAIRS: i32 = 10; - -#[embassy_executor::task] -/// Receives rpm from can handler, then computes and sends mph -pub async fn dti_handler( - rpm_recv: &'static Signal, - speed: &'static AtomicI32, -) { - loop { - let rpm_frame = rpm_recv.wait().await; - match rpm_frame.id() { - embassy_stm32::can::Id::Standard(id) => match id { - &DTI_RPM_MSG_ID => { - // TODO fat chance this works - let erpm = ((rpm_frame.data()[0] as i32) << 24u32) - + ((rpm_frame.data()[1] as i32) << 16) - + ((rpm_frame.data()[2] as i32) << 8u32) - + (rpm_frame.data()[3] as i32); - let mph = (erpm / POLE_PAIRS) as f32 / GEAR_RATIO - * 60.0 - * (TIRE_DIAMETER / 63360.0) - * PI; - // TODO add precision - speed.store(mph as i32, core::sync::atomic::Ordering::Release); - } - _ => (), - }, - embassy_stm32::can::Id::Extended(_) => (), - } - } -} diff --git a/cerberus/src/main.rs b/cerberus/src/main.rs deleted file mode 100644 index d582cc7..0000000 --- a/cerberus/src/main.rs +++ /dev/null @@ -1,234 +0,0 @@ -#![no_std] -#![no_main] -#![feature(impl_trait_in_assoc_type)] - -use core::{ - fmt::Write, - sync::atomic::{AtomicBool, AtomicI32}, -}; - -use cerberus::{ - bms, can_handler, dti, fault, monitor, state_machine, FaultCode, PduCommand, SharedI2c, - StateTransition, -}; -use cortex_m::{peripheral::SCB, singleton}; -use cortex_m_rt::{exception, ExceptionFrame}; -use defmt::{info, unwrap, warn}; -use embassy_executor::Spawner; -use embassy_stm32::{ - adc::{Adc, SampleTime, Sequence}, - bind_interrupts, - can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}, - exti::ExtiInput, - i2c::{self, I2c}, - peripherals::CAN1, - time::Hertz, -}; -use embassy_stm32::{ - can::Frame, - gpio::{Level, Output, Speed}, - peripherals, - usart::{self, Uart}, - wdg::IndependentWatchdog, - Config, -}; -use embassy_sync::{ - blocking_mutex::raw::{CriticalSectionRawMutex, ThreadModeRawMutex}, - channel::Channel, - mutex::Mutex, - signal::Signal, -}; -use embassy_time::Timer; -use heapless::String; -use static_cell::StaticCell; -use {defmt_rtt as _, panic_probe as _}; - -bind_interrupts!(struct IrqsCAN { - CAN1_RX0 => Rx0InterruptHandler; - CAN1_RX1 => Rx1InterruptHandler; - CAN1_SCE => SceInterruptHandler; - CAN1_TX => TxInterruptHandler; -}); - -bind_interrupts!(struct IrqsUsart { - USART3 => usart::InterruptHandler; -}); - -bind_interrupts!(struct IrqsI2c1 { - I2C1_EV => i2c::EventInterruptHandler; - I2C1_ER => i2c::ErrorInterruptHandler; -}); - -bind_interrupts!(struct IrqsI2c2 { - I2C2_EV => i2c::EventInterruptHandler; - I2C2_ER => i2c::ErrorInterruptHandler; -}); - -// channels to pass info with backpressure -static CAN_CHANNEL: Channel = Channel::new(); -static PDU_COMMAND: Channel = Channel::new(); - -// signals for most up to date state only - -static CURRENT_STATE: Signal = Signal::new(); -static FAULT: Signal = Signal::new(); - -// callbacks for CAN messages -static BMS_CALLBACK: Signal = Signal::new(); -static DTI_CALLBACK: Signal = Signal::new(); - -// state that is checked periodically rather than awaited - -// true=TS ON -static TSMS_SENSE: AtomicBool = AtomicBool::new(false); -// true=brakes engaged -static BRAKE_STATE: AtomicBool = AtomicBool::new(false); - -static DTI_MPH: AtomicI32 = AtomicI32::new(0); - -#[embassy_executor::main] -async fn main(spawner: Spawner) -> ! { - info!("Initializing Cerberus..."); - - let mut p = embassy_stm32::init(Config::default()); - - let can = Can::new(p.CAN1, p.PA11, p.PA12, IrqsCAN); - if let Err(err) = spawner.spawn(can_handler::can_handler( - can, - &BMS_CALLBACK, - &DTI_CALLBACK, - CAN_CHANNEL.receiver(), - )) { - warn!("Could not spawn CAN task: {}", err); - } - - if let Err(err) = spawner.spawn(bms::bms_handler(&BMS_CALLBACK, &FAULT)) { - warn!("Could not spawn BMS task: {}", err); - } - if let Err(err) = spawner.spawn(dti::dti_handler(&DTI_CALLBACK, &DTI_MPH)) { - warn!("Could not spawn DTI task: {}", err); - } - - if let Err(err) = spawner.spawn(fault::fault_handler( - CAN_CHANNEL.sender(), - &FAULT, - &CURRENT_STATE, - )) { - warn!("Could not spawn fault task: {}", err); - } - - static I2C_BUS_1: StaticCell = StaticCell::new(); - let i2c_1 = I2c::new( - p.I2C1, - p.PB6, - p.PB7, - IrqsI2c1, - p.DMA1_CH6, - p.DMA1_CH0, - Hertz(100_000), - i2c::Config::default(), - ); - let i2c_bus_1 = I2C_BUS_1.init(Mutex::new(i2c_1)); - - static I2C_BUS_2: StaticCell = StaticCell::new(); - let i2c_2 = I2c::new( - p.I2C2, - p.PB10, - p.PB11, - IrqsI2c2, - p.DMA1_CH7, - p.DMA1_CH2, - Hertz(100_000), - i2c::Config::default(), - ); - let i2c_bus_2 = I2C_BUS_2.init(Mutex::new(i2c_2)); - if let Err(err) = spawner.spawn(monitor::ctrl_expander_handler( - CAN_CHANNEL.sender(), - PDU_COMMAND.receiver(), - i2c_bus_2, - &TSMS_SENSE, - )) { - warn!("Could not spawn ctrl expander task: {}", err); - } - - const ADC1_BUF_SIZE: usize = 40; - let adc1 = Adc::new(p.ADC1); - let adc_data_1 = singleton!(ADCDAT : [u16; ADC1_BUF_SIZE] = [0u16; ADC1_BUF_SIZE]) - .expect("Could not init adc buffer"); - let mut adc1 = adc1.into_ring_buffered(p.DMA2_CH4, adc_data_1); - adc1.set_sample_sequence(Sequence::One, &mut p.PB0, SampleTime::CYCLES112); // LV sense - if let Err(err) = spawner.spawn(monitor::lv_sense_handler(adc1, CAN_CHANNEL.sender())) { - warn!("Could not spawn LV sense task: {}", err); - } - - const ADC3_BUF_SIZE: usize = 120; - let adc3 = Adc::new(p.ADC3); - let adc_data_3 = singleton!(ADCDAT : [u16; ADC3_BUF_SIZE] = [0u16; ADC3_BUF_SIZE]) - .expect("Could not init adc buffer"); - let mut adc3 = adc3.into_ring_buffered(p.DMA2_CH0, adc_data_3); - adc3.set_sample_sequence(Sequence::One, &mut p.PA0, SampleTime::CYCLES112); // - adc3.set_sample_sequence(Sequence::One, &mut p.PA1, SampleTime::CYCLES112); // - adc3.set_sample_sequence(Sequence::One, &mut p.PA2, SampleTime::CYCLES112); // - adc3.set_sample_sequence(Sequence::One, &mut p.PA3, SampleTime::CYCLES112); // - - let button1 = ExtiInput::new(p.PA4, p.EXTI4, embassy_stm32::gpio::Pull::Up); - let button2 = ExtiInput::new(p.PA5, p.EXTI5, embassy_stm32::gpio::Pull::Up); - let button3 = ExtiInput::new(p.PA6, p.EXTI6, embassy_stm32::gpio::Pull::Up); - let button4 = ExtiInput::new(p.PA7, p.EXTI7, embassy_stm32::gpio::Pull::Up); - //let button5 = ExtiInput::new(p.PC4, p.EXTI4, embassy_stm32::gpio::Pull::Up); - //let button6 = ExtiInput::new(p.PC5, p.EXTI5, embassy_stm32::gpio::Pull::Up); - let button7 = ExtiInput::new(p.PB0, p.EXTI0, embassy_stm32::gpio::Pull::Up); - let button8 = ExtiInput::new(p.PB1, p.EXTI1, embassy_stm32::gpio::Pull::Up); - if let Err(err) = spawner.spawn(monitor::steeringio_handler( - CAN_CHANNEL.sender(), - button1, - button2, - button3, - button4, - // button5, - // button6, - button7, - button8, - )) { - warn!("Could not spawn steeringIO task: {}", err); - } - - let mut usart = Uart::new( - p.USART3, - p.PC11, - p.PC10, - IrqsUsart, - p.DMA1_CH3, - p.DMA1_CH1, - usart::Config::default(), - ) - .unwrap(); - let mut s: String<128> = String::new(); - core::write!(&mut s, "Hello DMA World!\r\n",).unwrap(); - unwrap!(usart.write(s.as_bytes()).await); - - if let Err(err) = spawner.spawn(state_machine::state_handler( - &CURRENT_STATE, - PDU_COMMAND.sender(), - &DTI_MPH, - &BRAKE_STATE, - &TSMS_SENSE, - )) { - warn!("Could not spawn BMS task: {}", err); - } - - let mut watchdog = IndependentWatchdog::new(p.IWDG, 4000000); - watchdog.unleash(); - let mut led_pin = Output::new(p.PC8, Level::Low, Speed::Low); - loop { - info!("Status: Alive"); - led_pin.toggle(); - Timer::after_secs(3).await; - watchdog.pet(); - } -} - -#[exception] -unsafe fn HardFault(_frame: &ExceptionFrame) -> ! { - SCB::sys_reset() // <- you could do something other than reset -} diff --git a/crates/lsm6dso-ner/Cargo.toml b/crates/drivers/lsm6dso-ner/Cargo.toml similarity index 100% rename from crates/lsm6dso-ner/Cargo.toml rename to crates/drivers/lsm6dso-ner/Cargo.toml diff --git a/crates/lsm6dso-ner/src/lib.rs b/crates/drivers/lsm6dso-ner/src/lib.rs similarity index 100% rename from crates/lsm6dso-ner/src/lib.rs rename to crates/drivers/lsm6dso-ner/src/lib.rs diff --git a/crates/lsm6dso-ner/src/regs.rs b/crates/drivers/lsm6dso-ner/src/regs.rs similarity index 100% rename from crates/lsm6dso-ner/src/regs.rs rename to crates/drivers/lsm6dso-ner/src/regs.rs diff --git a/crates/pca9539-ner/Cargo.toml b/crates/drivers/pca9539-ner/Cargo.toml similarity index 100% rename from crates/pca9539-ner/Cargo.toml rename to crates/drivers/pca9539-ner/Cargo.toml diff --git a/crates/pca9539-ner/src/lib.rs b/crates/drivers/pca9539-ner/src/lib.rs similarity index 100% rename from crates/pca9539-ner/src/lib.rs rename to crates/drivers/pca9539-ner/src/lib.rs diff --git a/crates/sht3x-ner/Cargo.toml b/crates/drivers/sht3x-ner/Cargo.toml similarity index 100% rename from crates/sht3x-ner/Cargo.toml rename to crates/drivers/sht3x-ner/Cargo.toml diff --git a/crates/sht3x-ner/src/lib.rs b/crates/drivers/sht3x-ner/src/lib.rs similarity index 100% rename from crates/sht3x-ner/src/lib.rs rename to crates/drivers/sht3x-ner/src/lib.rs diff --git a/crates/vl6180x-ner/Cargo.toml b/crates/drivers/vl6180x-ner/Cargo.toml similarity index 100% rename from crates/vl6180x-ner/Cargo.toml rename to crates/drivers/vl6180x-ner/Cargo.toml diff --git a/crates/vl6180x-ner/src/config.rs b/crates/drivers/vl6180x-ner/src/config.rs similarity index 100% rename from crates/vl6180x-ner/src/config.rs rename to crates/drivers/vl6180x-ner/src/config.rs diff --git a/crates/vl6180x-ner/src/device_status.rs b/crates/drivers/vl6180x-ner/src/device_status.rs similarity index 100% rename from crates/vl6180x-ner/src/device_status.rs rename to crates/drivers/vl6180x-ner/src/device_status.rs diff --git a/crates/vl6180x-ner/src/error.rs b/crates/drivers/vl6180x-ner/src/error.rs similarity index 100% rename from crates/vl6180x-ner/src/error.rs rename to crates/drivers/vl6180x-ner/src/error.rs diff --git a/crates/vl6180x-ner/src/i2c_interface.rs b/crates/drivers/vl6180x-ner/src/i2c_interface.rs similarity index 100% rename from crates/vl6180x-ner/src/i2c_interface.rs rename to crates/drivers/vl6180x-ner/src/i2c_interface.rs diff --git a/crates/vl6180x-ner/src/init.rs b/crates/drivers/vl6180x-ner/src/init.rs similarity index 100% rename from crates/vl6180x-ner/src/init.rs rename to crates/drivers/vl6180x-ner/src/init.rs diff --git a/crates/vl6180x-ner/src/lib.rs b/crates/drivers/vl6180x-ner/src/lib.rs similarity index 100% rename from crates/vl6180x-ner/src/lib.rs rename to crates/drivers/vl6180x-ner/src/lib.rs diff --git a/crates/vl6180x-ner/src/mode.rs b/crates/drivers/vl6180x-ner/src/mode.rs similarity index 100% rename from crates/vl6180x-ner/src/mode.rs rename to crates/drivers/vl6180x-ner/src/mode.rs diff --git a/crates/vl6180x-ner/src/mode/continuous.rs b/crates/drivers/vl6180x-ner/src/mode/continuous.rs similarity index 100% rename from crates/vl6180x-ner/src/mode/continuous.rs rename to crates/drivers/vl6180x-ner/src/mode/continuous.rs diff --git a/crates/vl6180x-ner/src/mode/dynamic.rs b/crates/drivers/vl6180x-ner/src/mode/dynamic.rs similarity index 100% rename from crates/vl6180x-ner/src/mode/dynamic.rs rename to crates/drivers/vl6180x-ner/src/mode/dynamic.rs diff --git a/crates/vl6180x-ner/src/mode/powered_off.rs b/crates/drivers/vl6180x-ner/src/mode/powered_off.rs similarity index 100% rename from crates/vl6180x-ner/src/mode/powered_off.rs rename to crates/drivers/vl6180x-ner/src/mode/powered_off.rs diff --git a/crates/vl6180x-ner/src/mode/ready.rs b/crates/drivers/vl6180x-ner/src/mode/ready.rs similarity index 100% rename from crates/vl6180x-ner/src/mode/ready.rs rename to crates/drivers/vl6180x-ner/src/mode/ready.rs diff --git a/crates/vl6180x-ner/src/read_measurements.rs b/crates/drivers/vl6180x-ner/src/read_measurements.rs similarity index 100% rename from crates/vl6180x-ner/src/read_measurements.rs rename to crates/drivers/vl6180x-ner/src/read_measurements.rs diff --git a/crates/vl6180x-ner/src/register.rs b/crates/drivers/vl6180x-ner/src/register.rs similarity index 100% rename from crates/vl6180x-ner/src/register.rs rename to crates/drivers/vl6180x-ner/src/register.rs diff --git a/crates/vl6180x-ner/src/start_stop_measurements.rs b/crates/drivers/vl6180x-ner/src/start_stop_measurements.rs similarity index 100% rename from crates/vl6180x-ner/src/start_stop_measurements.rs rename to crates/drivers/vl6180x-ner/src/start_stop_measurements.rs diff --git a/projects/README.md b/projects/README.md new file mode 100644 index 0000000..fcabd19 --- /dev/null +++ b/projects/README.md @@ -0,0 +1,5 @@ +This is the location of all of the board-specific and high level operating code. + +Each folder is a project corresponding to ONE binary. + +Feel free to depend on code inside of crates/drivers folder. diff --git a/projects/cerberus/.cargo/config.toml b/projects/cerberus/.cargo/config.toml new file mode 100644 index 0000000..cc80e05 --- /dev/null +++ b/projects/cerberus/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "thumbv7em-none-eabi" diff --git a/cerberus/Cargo.toml b/projects/cerberus/Cargo.toml similarity index 67% rename from cerberus/Cargo.toml rename to projects/cerberus/Cargo.toml index 5c41df0..9b6baf0 100644 --- a/cerberus/Cargo.toml +++ b/projects/cerberus/Cargo.toml @@ -1,7 +1,10 @@ +cargo-features = ["per-package-target"] + [package] edition = "2021" name = "cerberus" version = "0.1.0" +forced-target = "thumbv7em-none-eabi" [dependencies] cortex-m.workspace = true @@ -10,7 +13,7 @@ defmt.workspace = true defmt-rtt.workspace = true embassy-embedded-hal.workspace = true embassy-executor.workspace = true -embassy-stm32.workspace = true +embassy-stm32 = { workspace = true, features = ["stm32f405rg"] } embassy-sync.workspace = true embassy-time.workspace = true embassy-futures.workspace = true @@ -18,4 +21,4 @@ heapless.workspace = true panic-probe.workspace = true static_cell.workspace = true bitfield.workspace = true -pca9539-ner = { version = "0.1.0", path = "../crates/pca9539-ner" } \ No newline at end of file +pca9539-ner = { version = "0.1.0", path = "../../crates/drivers/pca9539-ner" } diff --git a/cerberus/src/bms.rs b/projects/cerberus/src/bms.rs similarity index 100% rename from cerberus/src/bms.rs rename to projects/cerberus/src/bms.rs diff --git a/cerberus/src/can_handler.rs b/projects/cerberus/src/can_handler.rs similarity index 100% rename from cerberus/src/can_handler.rs rename to projects/cerberus/src/can_handler.rs diff --git a/projects/cerberus/src/dti.rs b/projects/cerberus/src/dti.rs new file mode 100644 index 0000000..bc5f561 --- /dev/null +++ b/projects/cerberus/src/dti.rs @@ -0,0 +1,37 @@ +use core::{f32::consts::PI, sync::atomic::AtomicI32}; + +use embassy_stm32::can::Frame; +use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal}; + +use crate::can_handler::DTI_RPM_MSG_ID; + +const TIRE_DIAMETER: f32 = 16.0; +const GEAR_RATIO: f32 = 47.0 / 13.0; +const POLE_PAIRS: i32 = 10; + +#[embassy_executor::task] +/// Receives rpm from can handler, then computes and sends mph +pub async fn dti_handler( + rpm_recv: &'static Signal, + speed: &'static AtomicI32, +) { + loop { + let rpm_frame = rpm_recv.wait().await; + match rpm_frame.id() { + embassy_stm32::can::Id::Standard(id) => if id == &DTI_RPM_MSG_ID { + // TODO fat chance this works + let erpm = ((rpm_frame.data()[0] as i32) << 24u32) + + ((rpm_frame.data()[1] as i32) << 16) + + ((rpm_frame.data()[2] as i32) << 8u32) + + (rpm_frame.data()[3] as i32); + let mph = (erpm / POLE_PAIRS) as f32 / GEAR_RATIO + * 60.0 + * (TIRE_DIAMETER / 63360.0) + * PI; + // TODO add precision + speed.store(mph as i32, core::sync::atomic::Ordering::Release); + }, + embassy_stm32::can::Id::Extended(_) => (), + } + } +} diff --git a/cerberus/src/fault.rs b/projects/cerberus/src/fault.rs similarity index 100% rename from cerberus/src/fault.rs rename to projects/cerberus/src/fault.rs diff --git a/cerberus/src/lib.rs b/projects/cerberus/src/lib.rs similarity index 91% rename from cerberus/src/lib.rs rename to projects/cerberus/src/lib.rs index 5b58b9d..07200a4 100644 --- a/cerberus/src/lib.rs +++ b/projects/cerberus/src/lib.rs @@ -1,6 +1,4 @@ #![no_std] -#![feature(const_option)] -#![feature(impl_trait_in_assoc_type)] pub mod bms; pub mod can_handler; @@ -11,7 +9,7 @@ pub mod state_machine; pub type SharedI2c = embassy_sync::mutex::Mutex< embassy_sync::blocking_mutex::raw::NoopRawMutex, - embassy_stm32::i2c::I2c<'static, embassy_stm32::mode::Async>, + embassy_stm32::i2c::I2c<'static, embassy_stm32::mode::Async, embassy_stm32::i2c::mode::Master>, >; #[derive(Copy, Clone, PartialEq, Eq)] diff --git a/projects/cerberus/src/main.rs b/projects/cerberus/src/main.rs new file mode 100644 index 0000000..3d3bb6f --- /dev/null +++ b/projects/cerberus/src/main.rs @@ -0,0 +1,262 @@ +#![no_std] +#![no_main] + +use core::{ + fmt::Write, + sync::atomic::{AtomicBool, AtomicI32}, +}; + +use cerberus::{ + bms, can_handler, dti, fault, monitor, state_machine, FaultCode, PduCommand, SharedI2c, + StateTransition, +}; +use cortex_m::{peripheral::SCB, singleton}; +use cortex_m_rt::{exception, ExceptionFrame}; +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_stm32::{ + adc::{Adc, AdcChannel, SampleTime, CONTINUOUS}, + bind_interrupts, + can::{Can, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, TxInterruptHandler}, + dma, + exti::{self}, + i2c::{self, I2c}, + interrupt, + peripherals::CAN1, +}; +use embassy_stm32::{ + can::Frame, + gpio::{Level, Output, Speed}, + peripherals, + usart::{self, Uart}, + wdg::IndependentWatchdog, + Config, +}; +use embassy_sync::{ + blocking_mutex::raw::{CriticalSectionRawMutex, ThreadModeRawMutex}, + channel::Channel, + mutex::Mutex, + signal::Signal, +}; +use embassy_time::Timer; +use heapless::String; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct IrqsCAN { + CAN1_RX0 => Rx0InterruptHandler; + CAN1_RX1 => Rx1InterruptHandler; + CAN1_SCE => SceInterruptHandler; + CAN1_TX => TxInterruptHandler; +}); + +bind_interrupts!(struct IrqsUsart { + USART3 => usart::InterruptHandler; + DMA1_STREAM1 => dma::InterruptHandler; + DMA1_STREAM3 => dma::InterruptHandler; +}); + +bind_interrupts!(struct IrqsI2c1 { + I2C1_EV => i2c::EventInterruptHandler; + I2C1_ER => i2c::ErrorInterruptHandler; + DMA1_STREAM0 => dma::InterruptHandler; + DMA1_STREAM6 => dma::InterruptHandler; +}); + +bind_interrupts!(struct IrqsI2c2 { + I2C2_EV => i2c::EventInterruptHandler; + I2C2_ER => i2c::ErrorInterruptHandler; + DMA1_STREAM2 => dma::InterruptHandler; + DMA1_STREAM7 => dma::InterruptHandler; +}); + +bind_interrupts!(struct IrqsAdc2 { + DMA2_STREAM4 => dma::InterruptHandler; +}); +bind_interrupts!(struct IrqsAdc1 { + DMA2_STREAM0 => dma::InterruptHandler; +}); + +bind_interrupts!(struct IrqsExti4 { + EXTI4 => exti::InterruptHandler; +}); +bind_interrupts!(struct IrqsExti5 { + EXTI9_5 => exti::InterruptHandler; +}); +bind_interrupts!(struct IrqsExti0 { + EXTI0 => exti::InterruptHandler; +}); +bind_interrupts!(struct IrqsExti1 { + EXTI1 => exti::InterruptHandler; +}); +// channels to pass info with backpressure +static CAN_CHANNEL: Channel = Channel::new(); +static PDU_COMMAND: Channel = Channel::new(); + +// signals for most up to date state only + +static CURRENT_STATE: Signal = Signal::new(); +static FAULT: Signal = Signal::new(); + +// callbacks for CAN messages +static BMS_CALLBACK: Signal = Signal::new(); +static DTI_CALLBACK: Signal = Signal::new(); + +// state that is checked periodically rather than awaited + +// true=TS ON +static TSMS_SENSE: AtomicBool = AtomicBool::new(false); +// true=brakes engaged +static BRAKE_STATE: AtomicBool = AtomicBool::new(false); + +static DTI_MPH: AtomicI32 = AtomicI32::new(0); + +#[embassy_executor::main] +async fn main(spawner: Spawner) -> ! { + info!("Initializing Cerberus..."); + + let p = embassy_stm32::init(Config::default()); + + let can = Can::new(p.CAN1, p.PA11, p.PA12, IrqsCAN); + spawner.spawn( + can_handler::can_handler(can, &BMS_CALLBACK, &DTI_CALLBACK, CAN_CHANNEL.receiver()) + .unwrap(), + ); + + spawner.spawn(bms::bms_handler(&BMS_CALLBACK, &FAULT).unwrap()); + spawner.spawn(dti::dti_handler(&DTI_CALLBACK, &DTI_MPH).unwrap()); + + spawner.spawn(fault::fault_handler(CAN_CHANNEL.sender(), &FAULT, &CURRENT_STATE).unwrap()); + + // static I2C_BUS_1: StaticCell = StaticCell::new(); + // let i2c_1 = I2c::new( + // p.I2C1, + // p.PB6, + // p.PB7, + // p.DMA1_CH6, + // p.DMA1_CH0, + // IrqsI2c1, + // i2c::Config::default(), + // ); + //let i2c_bus_1 = I2C_BUS_1.init(Mutex::new(i2c_1)); + + static I2C_BUS_2: StaticCell = StaticCell::new(); + let i2c_2 = I2c::new( + p.I2C2, + p.PB10, + p.PB11, + p.DMA1_CH7, + p.DMA1_CH2, + IrqsI2c2, + i2c::Config::default(), + ); + let i2c_bus_2 = I2C_BUS_2.init(Mutex::new(i2c_2)); + spawner.spawn( + monitor::ctrl_expander_handler( + CAN_CHANNEL.sender(), + PDU_COMMAND.receiver(), + i2c_bus_2, + &TSMS_SENSE, + ) + .unwrap(), + ); + + const ADC1_BUF_SIZE: usize = 40; + let adc1 = Adc::new(p.ADC1); + let adc_data_1 = singleton!(ADCDAT : [u16; ADC1_BUF_SIZE] = [0u16; ADC1_BUF_SIZE]) + .expect("Could not init adc buffer"); + let adc1 = adc1.into_ring_buffered( + p.DMA2_CH4, + adc_data_1, + IrqsAdc2, + [ + (p.PB0.degrade_adc(), SampleTime::CYCLES112), // LV sense + ] + .into_iter(), + CONTINUOUS, + embassy_stm32::adc::Exten::DISABLED, + ); + spawner.spawn(monitor::lv_sense_handler(adc1, CAN_CHANNEL.sender()).unwrap()); + + // const ADC3_BUF_SIZE: usize = 120; + // let adc3 = Adc::new(p.ADC3); + // let adc_data_3 = singleton!(ADCDAT : [u16; ADC3_BUF_SIZE] = [0u16; ADC3_BUF_SIZE]) + // .expect("Could not init adc buffer"); + // let adc3 = adc3.into_ring_buffered( + // p.DMA2_CH0, + // adc_data_3, + // IrqsAdc1, + // [ + // (p.PA0.degrade_adc(), SampleTime::CYCLES112), + // (p.PA1.degrade_adc(), SampleTime::CYCLES112), + // (p.PA2.degrade_adc(), SampleTime::CYCLES112), + // (p.PA3.degrade_adc(), SampleTime::CYCLES112), + // ] + // .into_iter(), + // CONTINUOUS, + // embassy_stm32::adc::Exten::DISABLED, + // ); + + // let button1 = ExtiInput::new(p.PA4, p.EXTI4, embassy_stm32::gpio::Pull::Up, IrqsExti4); + // let button2 = ExtiInput::new(p.PA5, p.EXTI5, embassy_stm32::gpio::Pull::Up, IrqsExti5); + // let button3 = ExtiInput::new(p.PA6, p.EXTI6, embassy_stm32::gpio::Pull::Up, IrqsExti5); + // let button4 = ExtiInput::new(p.PA7, p.EXTI7, embassy_stm32::gpio::Pull::Up, IrqsExti5); + // //let button5 = ExtiInput::new(p.PC4, p.EXTI4, embassy_stm32::gpio::Pull::Up); + // //let button6 = ExtiInput::new(p.PC5, p.EXTI5, embassy_stm32::gpio::Pull::Up); + // let button7 = ExtiInput::new(p.PB0, p.EXTI0, embassy_stm32::gpio::Pull::Up, IrqsExti0); + // let button8 = ExtiInput::new(p.PB1, p.EXTI1, embassy_stm32::gpio::Pull::Up, IrqsExti1); + // spawner.spawn( + // monitor::steeringio_handler( + // CAN_CHANNEL.sender(), + // button1, + // button2, + // button3, + // button4, + // // button5, + // // button6, + // button7, + // button8, + // ) + // .unwrap(), + // ); + + let mut usart = Uart::new( + p.USART3, + p.PC11, + p.PC10, + p.DMA1_CH3, + p.DMA1_CH1, + IrqsUsart, + usart::Config::default(), + ) + .unwrap(); + let mut s: String<128> = String::new(); + core::write!(&mut s, "Hello DMA World!\r\n",).unwrap(); + unwrap!(usart.write(s.as_bytes()).await); + + spawner.spawn( + state_machine::state_handler( + &CURRENT_STATE, + PDU_COMMAND.sender(), + &DTI_MPH, + &BRAKE_STATE, + &TSMS_SENSE, + ) + .unwrap(), + ); + + let mut watchdog = IndependentWatchdog::new(p.IWDG, 4000000); + watchdog.unleash(); + let mut led_pin = Output::new(p.PC8, Level::Low, Speed::Low); + loop { + info!("Status: Alive"); + led_pin.toggle(); + Timer::after_secs(3).await; + watchdog.pet(); + } +} + +#[exception] +unsafe fn HardFault(_frame: &ExceptionFrame) -> ! { + SCB::sys_reset() // <- you could do something other than reset +} diff --git a/cerberus/src/monitor.rs b/projects/cerberus/src/monitor.rs similarity index 89% rename from cerberus/src/monitor.rs rename to projects/cerberus/src/monitor.rs index ea54261..dfb3a19 100644 --- a/cerberus/src/monitor.rs +++ b/projects/cerberus/src/monitor.rs @@ -1,13 +1,14 @@ use core::sync::atomic::AtomicBool; -use bitfield::Bit; -use defmt::{unwrap, warn}; +use bitfield::{Bit, BitMut}; +use defmt::unwrap; use embassy_embedded_hal::shared_bus::asynch::i2c::I2cDevice; use embassy_futures::select::{self, select3, select_array}; use embassy_stm32::{ adc::RingBufferedAdc, can::{Frame, StandardId}, exti::ExtiInput, + mode::Async, peripherals::ADC1, }; use embassy_sync::{ @@ -31,24 +32,17 @@ pub async fn lv_sense_handler( let mut measurements: [u16; 20] = [0u16; 40 / 2]; loop { - match adc1.read(&mut measurements).await { - Ok(_) => { - adc1.teardown_adc(); - // 8.97 is mahic no. bc nobody remembers the exact resistor config - let v_in = (measurements[0] as f32 * 8.967 * 10f32) as u32; - // TODO transform measurements - can_send - .send(unwrap!(Frame::new_data( - LV_SENSE_MSG_ID, - &v_in.to_be_bytes() - ))) - .await; - } - Err(_) => { - warn!("DMA overrun"); - continue; - } - } + adc1.read_latest(&mut measurements); + // 8.97 is mahic no. bc nobody remembers the exact resistor config + let v_in = (measurements[0] as f32 * 8.967 * 10f32) as u32; + // TODO transform measurements + can_send + .send(unwrap!(Frame::new_data( + LV_SENSE_MSG_ID, + &v_in.to_be_bytes() + ))) + .await; + Timer::after(LV_SENSE_REFRESH_TIME).await; } } @@ -248,15 +242,15 @@ pub async fn ctrl_expander_handler( #[embassy_executor::task] pub async fn steeringio_handler( - can_send: Sender<'static, ThreadModeRawMutex, Frame, 25>, - mut button1: ExtiInput<'static>, - mut button2: ExtiInput<'static>, - mut button3: ExtiInput<'static>, - mut button4: ExtiInput<'static>, + _can_send: Sender<'static, ThreadModeRawMutex, Frame, 25>, + mut button1: ExtiInput<'static, Async>, + mut button2: ExtiInput<'static, Async>, + mut button3: ExtiInput<'static, Async>, + mut button4: ExtiInput<'static, Async>, // mut button5: ExtiInput<'static>, // mut button6: ExtiInput<'static>, - mut button7: ExtiInput<'static>, - mut button8: ExtiInput<'static>, + mut button7: ExtiInput<'static, Async>, + mut button8: ExtiInput<'static, Async>, ) { loop { let ans = select_array([ @@ -271,7 +265,7 @@ pub async fn steeringio_handler( ]) .await; - let button_value = match ans.1 { + let _button_value = match ans.1 { 0 => button1.get_level(), 1 => button2.get_level(), 2 => button3.get_level(), diff --git a/cerberus/src/state_machine.rs b/projects/cerberus/src/state_machine.rs similarity index 96% rename from cerberus/src/state_machine.rs rename to projects/cerberus/src/state_machine.rs index 85144e3..446cb81 100644 --- a/cerberus/src/state_machine.rs +++ b/projects/cerberus/src/state_machine.rs @@ -19,7 +19,7 @@ pub async fn state_handler( tsms_status: &'static AtomicBool, ) { let mut prev_func_state = FunctionalType::READY; - let mut prev_nero_state = NeroType::OFF; + //let mut prev_nero_state = NeroType::OFF; loop { let new_state = state_recv.wait().await; @@ -80,7 +80,7 @@ pub async fn state_handler( match new_state { StateTransition::Functional(f) => prev_func_state = f, - StateTransition::Nero(n) => prev_nero_state = n, + StateTransition::Nero(_) => (), //prev_nero_state = n, } } } diff --git a/projects/lightning/.cargo/config.toml b/projects/lightning/.cargo/config.toml new file mode 100644 index 0000000..6ae2691 --- /dev/null +++ b/projects/lightning/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "thumbv8m.main-none-eabihf" diff --git a/projects/lightning/Cargo.toml b/projects/lightning/Cargo.toml new file mode 100644 index 0000000..a4309d9 --- /dev/null +++ b/projects/lightning/Cargo.toml @@ -0,0 +1,22 @@ +cargo-features = ["per-package-target"] + +[package] +edition = "2024" +name = "lightning" +version = "0.1.0" +forced-target = "thumbv7em-none-eabi" + +[dependencies] +cortex-m.workspace = true +cortex-m-rt.workspace = true +defmt.workspace = true +defmt-rtt.workspace = true +embassy-embedded-hal.workspace = true +embassy-executor.workspace = true +embassy-stm32 = { workspace = true, features = ["stm32h563zi"] } +embassy-sync.workspace = true +embassy-time.workspace = true +embassy-futures.workspace = true +heapless.workspace = true +panic-probe.workspace = true +#static_cell.workspace = true diff --git a/projects/lightning/build.rs b/projects/lightning/build.rs new file mode 100644 index 0000000..8cd32d7 --- /dev/null +++ b/projects/lightning/build.rs @@ -0,0 +1,5 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/projects/lightning/src/main.rs b/projects/lightning/src/main.rs new file mode 100644 index 0000000..5c34a40 --- /dev/null +++ b/projects/lightning/src/main.rs @@ -0,0 +1,58 @@ +#![no_std] +#![no_main] + +use core::fmt::Write; +use cortex_m::peripheral::SCB; +use cortex_m_rt::{ExceptionFrame, exception}; +use defmt::debug; +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_stm32::usart::Uart; +use embassy_stm32::{Config, dma, peripherals, usart}; +use embassy_stm32::{bind_interrupts, wdg::IndependentWatchdog}; +use embassy_time::Timer; +use heapless::String; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct IrqsUsart { + LPUART1 => usart::InterruptHandler; + GPDMA1_CHANNEL0 => dma::InterruptHandler; + GPDMA1_CHANNEL1 => dma::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + info!("Initializing wheel..."); + // initialize the project, ensure we can debug during sleep + let p = embassy_stm32::init(Config::default()); + + let mut usart_config = usart::Config::default(); + usart_config.swap_rx_tx = true; + let mut usart = Uart::new( + p.LPUART1, + p.PA10, + p.PA9, + p.GPDMA1_CH0, + p.GPDMA1_CH1, + IrqsUsart, + usart_config, + ) + .unwrap(); + + let mut s: String<128> = String::new(); + core::write!(&mut s, "MSB-FW.rs prints in RTT, not UART!\r\n",).unwrap(); + unwrap!(usart.write(s.as_bytes()).await); + + let mut watchdog = IndependentWatchdog::new(p.IWDG, 1000000); + watchdog.unleash(); + loop { + debug!("Status: Alive"); + Timer::after_millis(500).await; + watchdog.pet(); + } +} + +#[exception] +unsafe fn HardFault(_frame: &ExceptionFrame) -> ! { + SCB::sys_reset() // <- you could do something other than reset +} diff --git a/projects/msb-fw-rs/.cargo/config.toml b/projects/msb-fw-rs/.cargo/config.toml new file mode 100644 index 0000000..cc80e05 --- /dev/null +++ b/projects/msb-fw-rs/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "thumbv7em-none-eabi" diff --git a/msb-fw-rs/Cargo.toml b/projects/msb-fw-rs/Cargo.toml similarity index 56% rename from msb-fw-rs/Cargo.toml rename to projects/msb-fw-rs/Cargo.toml index e4b16aa..16e30da 100644 --- a/msb-fw-rs/Cargo.toml +++ b/projects/msb-fw-rs/Cargo.toml @@ -1,7 +1,10 @@ +cargo-features = ["per-package-target"] + [package] edition = "2024" name = "msb-fw-rs" version = "0.1.0" +forced-target = "thumbv7em-none-eabi" [dependencies] cortex-m.workspace = true @@ -10,15 +13,15 @@ defmt.workspace = true defmt-rtt.workspace = true embassy-embedded-hal.workspace = true embassy-executor.workspace = true -embassy-stm32.workspace = true +embassy-stm32 = { workspace = true, features = ["stm32f405rg"] } embassy-sync.workspace = true embassy-time.workspace = true heapless.workspace = true -lsm6dso-ner = { version = "0.1.0", path = "../crates/lsm6dso-ner", optional = true } +lsm6dso-ner = { version = "0.1.0", path = "../../crates/drivers/lsm6dso-ner", optional = true } panic-probe.workspace = true -sht3x-ner = { version = "0.1.0", path = "../crates/sht3x-ner", optional = true } +sht3x-ner = { version = "0.1.0", path = "../../crates/drivers/sht3x-ner", optional = true } static_cell.workspace = true -vl6180x-ner = { version = "0.1.0", path = "../crates/vl6180x-ner", optional = true } +vl6180x-ner = { version = "0.1.0", path = "../../crates/drivers/vl6180x-ner", optional = true } [features] default = ["temp-sensor", "imu-sensor"] diff --git a/msb-fw-rs/build.rs b/projects/msb-fw-rs/build.rs similarity index 100% rename from msb-fw-rs/build.rs rename to projects/msb-fw-rs/build.rs diff --git a/msb-fw-rs/src/can_handler.rs b/projects/msb-fw-rs/src/can_handler.rs similarity index 100% rename from msb-fw-rs/src/can_handler.rs rename to projects/msb-fw-rs/src/can_handler.rs diff --git a/msb-fw-rs/src/controllers.rs b/projects/msb-fw-rs/src/controllers.rs similarity index 100% rename from msb-fw-rs/src/controllers.rs rename to projects/msb-fw-rs/src/controllers.rs diff --git a/msb-fw-rs/src/lib.rs b/projects/msb-fw-rs/src/lib.rs similarity index 100% rename from msb-fw-rs/src/lib.rs rename to projects/msb-fw-rs/src/lib.rs diff --git a/msb-fw-rs/src/main.rs b/projects/msb-fw-rs/src/main.rs similarity index 100% rename from msb-fw-rs/src/main.rs rename to projects/msb-fw-rs/src/main.rs diff --git a/msb-fw-rs/src/readers.rs b/projects/msb-fw-rs/src/readers.rs similarity index 100% rename from msb-fw-rs/src/readers.rs rename to projects/msb-fw-rs/src/readers.rs diff --git a/projects/wheel/.cargo/config.toml b/projects/wheel/.cargo/config.toml new file mode 100644 index 0000000..cc80e05 --- /dev/null +++ b/projects/wheel/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "thumbv7em-none-eabi" diff --git a/wheel/Cargo.toml b/projects/wheel/Cargo.toml similarity index 74% rename from wheel/Cargo.toml rename to projects/wheel/Cargo.toml index cbef710..eeac46a 100644 --- a/wheel/Cargo.toml +++ b/projects/wheel/Cargo.toml @@ -1,7 +1,10 @@ +cargo-features = ["per-package-target"] + [package] edition = "2021" name = "wheel" version = "0.1.0" +forced-target = "thumbv8m.main-none-eabihf" [dependencies] cortex-m.workspace = true @@ -10,7 +13,7 @@ defmt.workspace = true defmt-rtt.workspace = true embassy-embedded-hal.workspace = true embassy-executor.workspace = true -embassy-stm32.workspace = true +embassy-stm32 = { workspace = true, features = ["stm32f405rg"] } embassy-sync.workspace = true embassy-time.workspace = true embassy-futures.workspace = true diff --git a/projects/wheel/build.rs b/projects/wheel/build.rs new file mode 100644 index 0000000..ddbc5ef --- /dev/null +++ b/projects/wheel/build.rs @@ -0,0 +1,6 @@ +fn main() { + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); + println!("cargo:rerun-if-changed=memory.x"); +} diff --git a/wheel/src/main.rs b/projects/wheel/src/main.rs similarity index 80% rename from wheel/src/main.rs rename to projects/wheel/src/main.rs index 55cca3e..77dcd42 100644 --- a/wheel/src/main.rs +++ b/projects/wheel/src/main.rs @@ -1,7 +1,5 @@ #![no_std] #![no_main] -#![feature(impl_trait_in_assoc_type)] -#![feature(const_option)] use cortex_m::peripheral::SCB; use cortex_m_rt::{exception, ExceptionFrame}; @@ -14,8 +12,10 @@ use embassy_stm32::{ Can, Frame, Rx0InterruptHandler, Rx1InterruptHandler, SceInterruptHandler, StandardId, TxInterruptHandler, }, + exti, exti::ExtiInput, gpio::Level, + interrupt, peripherals::CAN1, }; use embassy_stm32::{ @@ -38,6 +38,25 @@ bind_interrupts!(struct IrqsUsart { USART2 => usart::InterruptHandler; }); +bind_interrupts!(struct IrqsExti4 { + EXTI4 => exti::InterruptHandler; +}); +bind_interrupts!(struct IrqsExti5 { + EXTI9_5 => exti::InterruptHandler; +}); +bind_interrupts!(struct IrqsExti0 { + EXTI0 => exti::InterruptHandler; +}); +bind_interrupts!(struct IrqsExti1 { + EXTI1 => exti::InterruptHandler; +}); +bind_interrupts!(struct IrqsExti2 { + EXTI2 => exti::InterruptHandler; +}); +bind_interrupts!(struct IrqsExti3 { + EXTI3 => exti::InterruptHandler; +}); + const SEND_MSG_ID: StandardId = StandardId::new(0x680).expect("Could not parse ID"); // main should be where the peripheral object is used, and then peripherals are init-ed and sent to the threads @@ -69,12 +88,12 @@ async fn main(_spawner: Spawner) -> ! { // core::write!(&mut s, "Hello DMA World!\r\n",).unwrap(); // unwrap!(usart.write(s.as_bytes()).await); - let mut button1 = ExtiInput::new(p.PA1, p.EXTI1, embassy_stm32::gpio::Pull::Up); - let mut button2 = ExtiInput::new(p.PA2, p.EXTI2, embassy_stm32::gpio::Pull::Up); - let mut button3 = ExtiInput::new(p.PA3, p.EXTI3, embassy_stm32::gpio::Pull::Up); - let mut button4 = ExtiInput::new(p.PA4, p.EXTI4, embassy_stm32::gpio::Pull::Up); - let mut button5 = ExtiInput::new(p.PC5, p.EXTI5, embassy_stm32::gpio::Pull::Up); - let mut button6 = ExtiInput::new(p.PC6, p.EXTI6, embassy_stm32::gpio::Pull::Up); + let mut button1 = ExtiInput::new(p.PA1, p.EXTI1, embassy_stm32::gpio::Pull::Up, IrqsExti1); + let mut button2 = ExtiInput::new(p.PA2, p.EXTI2, embassy_stm32::gpio::Pull::Up, IrqsExti2); + let mut button3 = ExtiInput::new(p.PA3, p.EXTI3, embassy_stm32::gpio::Pull::Up, IrqsExti3); + let mut button4 = ExtiInput::new(p.PA4, p.EXTI4, embassy_stm32::gpio::Pull::Up, IrqsExti4); + let mut button5 = ExtiInput::new(p.PC5, p.EXTI5, embassy_stm32::gpio::Pull::Up, IrqsExti5); + let mut button6 = ExtiInput::new(p.PC6, p.EXTI6, embassy_stm32::gpio::Pull::Up, IrqsExti5); loop { select_array([ diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 87a0892..00c77b5 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,5 +1,5 @@ # The channel key should match the stable rust-toolchain.toml in the embassy repo [toolchain] -channel = "1.92" -components = [ "rust-src", "rustfmt", "llvm-tools", "clippy" ] -targets = [ "thumbv7em-none-eabi" ] +channel = "nightly-2025-12-11" +components = [ "rust-src", "rustfmt", "llvm-tools", "clippy", "miri" ] +targets = [ "thumbv7em-none-eabi", "thumbv8m.main-none-eabihf" ] From 1daee7708743107e2ca7c765e682a782c6ef3973 Mon Sep 17 00:00:00 2001 From: Jack Rubacha Date: Tue, 12 May 2026 23:46:18 -0400 Subject: [PATCH 2/3] fmt, note --- README.md | 2 ++ projects/cerberus/src/dti.rs | 28 +++++++++++++++------------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 360d431..6468146 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,8 @@ NER Firmware in Rust (experimental, not for usage on car) - To run a GDB terminal dedicated: `cargo embed --release gdb` - To flash and leave code: `cargo embed --release` +**At this time, many commands only work consistently inside the project (via `cd`)** +The workspace's only purpose is to organize dependencies and build artificats, especially for rust-analyzer. ### Coding tips and tricks diff --git a/projects/cerberus/src/dti.rs b/projects/cerberus/src/dti.rs index bc5f561..5b99c64 100644 --- a/projects/cerberus/src/dti.rs +++ b/projects/cerberus/src/dti.rs @@ -18,19 +18,21 @@ pub async fn dti_handler( loop { let rpm_frame = rpm_recv.wait().await; match rpm_frame.id() { - embassy_stm32::can::Id::Standard(id) => if id == &DTI_RPM_MSG_ID { - // TODO fat chance this works - let erpm = ((rpm_frame.data()[0] as i32) << 24u32) - + ((rpm_frame.data()[1] as i32) << 16) - + ((rpm_frame.data()[2] as i32) << 8u32) - + (rpm_frame.data()[3] as i32); - let mph = (erpm / POLE_PAIRS) as f32 / GEAR_RATIO - * 60.0 - * (TIRE_DIAMETER / 63360.0) - * PI; - // TODO add precision - speed.store(mph as i32, core::sync::atomic::Ordering::Release); - }, + embassy_stm32::can::Id::Standard(id) => { + if id == &DTI_RPM_MSG_ID { + // TODO fat chance this works + let erpm = ((rpm_frame.data()[0] as i32) << 24u32) + + ((rpm_frame.data()[1] as i32) << 16) + + ((rpm_frame.data()[2] as i32) << 8u32) + + (rpm_frame.data()[3] as i32); + let mph = (erpm / POLE_PAIRS) as f32 / GEAR_RATIO + * 60.0 + * (TIRE_DIAMETER / 63360.0) + * PI; + // TODO add precision + speed.store(mph as i32, core::sync::atomic::Ordering::Release); + } + } embassy_stm32::can::Id::Extended(_) => (), } } From 11f1e8fb5c757ab4b3eaa62aa1c315f79fd60c7b Mon Sep 17 00:00:00 2001 From: Jack Rubacha Date: Wed, 13 May 2026 00:49:38 -0400 Subject: [PATCH 3/3] hopefully my probe-rs commit is merged --- .cargo/config.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/.cargo/config.toml b/.cargo/config.toml index 415d916..ba36fc0 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -6,6 +6,7 @@ runner = 'probe-rs run --chip STM32F405RGTx' [env] DEFMT_LOG = "warn" +PROBE_RS_EMBED_FILE = { value = "Embed.toml", relative = true } # these are all so the workspace can work cohesively, and should not affect code stability