diff --git a/Cargo.toml b/Cargo.toml index 41328b51..fe5c4703 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -74,6 +74,7 @@ embedded-sdmmc = "0.3.0" usb-device = { version = "0.3.2", features = ["defmt"] } usbd-serial = "0.2.2" rand = { version = "0.9", default-features = false } +as5600 = "0.8.0" #TODO: Separate feature sets [features] @@ -84,9 +85,23 @@ usb = ["dep:stm32-usbd"] stm32g431 = ["stm32g4/stm32g431", "cat2"] stm32g441 = ["stm32g4/stm32g441", "cat2"] stm32g473 = ["stm32g4/stm32g473", "cat3", "adc3", "adc4", "adc5"] -stm32g474 = ["stm32g4/stm32g474", "cat3", "adc3", "adc4", "adc5", "stm32-hrtim/stm32g474"] +stm32g474 = [ + "stm32g4/stm32g474", + "cat3", + "adc3", + "adc4", + "adc5", + "stm32-hrtim/stm32g474", +] stm32g483 = ["stm32g4/stm32g483", "cat3", "adc3", "adc4", "adc5"] -stm32g484 = ["stm32g4/stm32g484", "cat3", "adc3", "adc4", "adc5", "stm32-hrtim/stm32g484"] +stm32g484 = [ + "stm32g4/stm32g484", + "cat3", + "adc3", + "adc4", + "adc5", + "stm32-hrtim/stm32g484", +] stm32g491 = ["stm32g4/stm32g491", "cat4", "adc3"] stm32g4a1 = ["stm32g4/stm32g4a1", "cat4", "adc3"] @@ -108,7 +123,7 @@ defmt = [ "embedded-hal/defmt-03", "embedded-io/defmt-03", "embedded-test/defmt", - "stm32-hrtim?/defmt" + "stm32-hrtim?/defmt", ] cordic = ["dep:fixed"] adc3 = [] diff --git a/Embed.toml b/Embed.toml new file mode 100644 index 00000000..74bc2415 --- /dev/null +++ b/Embed.toml @@ -0,0 +1,2 @@ +[default.rtt] +enabled = true diff --git a/examples/button.rs b/examples/button.rs index c3530b90..a1c12a9d 100644 --- a/examples/button.rs +++ b/examples/button.rs @@ -5,14 +5,13 @@ use stm32g4xx_hal::{ //delay::{DelayExt, SYSTDelayExt}, gpio::{self, ExtiPin, GpioExt, Input, SignalEdge}, rcc::RccExt, - stm32, - stm32::{interrupt, Interrupt}, + stm32::{self, interrupt, Interrupt}, syscfg::SysCfgExt, }; use core::cell::RefCell; use core::sync::atomic::{AtomicBool, Ordering}; -use cortex_m::{asm::wfi, interrupt::Mutex}; +use cortex_m::interrupt::Mutex; use cortex_m_rt::entry; type ButtonPin = gpio::PC13; @@ -52,8 +51,14 @@ fn main() -> ! { utils::logger::init(); let mut dp = stm32::Peripherals::take().expect("cannot take peripherals"); + let mut rcc = dp.RCC.constrain(); - let mut syscfg = dp.SYSCFG.constrain(); + + // Workaround for RTT when using wfi instruction + // Enable an AHB peripheral clock for debug probe with wfi + rcc.ahb1enr().modify(|_, w| w.dma1en().set_bit()); + + let mut syscfg = dp.SYSCFG.constrain(&mut rcc); println!("Led Init"); // Configure PA5 pin to blink LED @@ -80,7 +85,7 @@ fn main() -> ! { println!("Start Loop"); loop { - wfi(); + cortex_m::asm::wfi(); println!("Check"); if G_LED_ON.load(Ordering::Relaxed) { diff --git a/examples/i2c-fmp-as5600.rs b/examples/i2c-fmp-as5600.rs new file mode 100644 index 00000000..2031558e --- /dev/null +++ b/examples/i2c-fmp-as5600.rs @@ -0,0 +1,92 @@ +//! I2C Fast Mode Plus example with an AS5600 magnetic angle sensor. +//! +//! This example expects the AS5600 to be connected to the I2C bus on PB7 (SDA) and PA15 (SCL), +//! and a 24MHz HSE oscillator to configure the PLL for 168MHz system clock. +//! +//! The I2C bus is configured with Fast Mode Plus (FMP) enabled in SysCfg, and a 1MHz I2C clock rate. +//! +//! ```DEFMT_LOG=debug cargo run --release --example i2c-fmp-as5600 --features stm32g431,defmt -- --chip STM32G431KBTx``` + +#![deny(warnings)] +#![deny(unsafe_code)] +#![no_main] +#![no_std] + +use fugit::HertzU32 as Hertz; +use hal::prelude::*; +use hal::rcc::SysClockSrc; +use hal::stm32; +use hal::time::RateExtU32; +use stm32g4xx_hal as hal; +use stm32g4xx_hal::syscfg::SysCfgExt; + +use as5600::As5600; +use cortex_m_rt::entry; + +#[macro_use] +mod utils; +use utils::logger::error; +use utils::logger::info; + +#[entry] +fn main() -> ! { + utils::logger::init(); + + let dp = stm32::Peripherals::take().expect("cannot take peripherals"); + //let cp = cortex_m::Peripherals::take().expect("cannot take core peripherals"); + + let pwr_cfg = dp + .PWR + .constrain() + .vos(stm32g4xx_hal::pwr::VoltageScale::Range1 { enable_boost: true }) + .freeze(); + + let pll_cfg = hal::rcc::PllConfig { + mux: hal::rcc::PllSrc::HSE(Hertz::MHz(24)), + m: hal::rcc::PllMDiv::DIV_2, + n: hal::rcc::PllNMul::MUL_28, + r: Some(hal::rcc::PllRDiv::DIV_2), + q: None, + p: None, + }; + + info!("Configuring PLL"); + let rcc_cfg = hal::rcc::Config::new(SysClockSrc::PLL) + .boost(true) + .pll_cfg(pll_cfg); + + let mut rcc = dp.RCC.freeze(rcc_cfg, pwr_cfg); + info!("System clock frequency: {}", rcc.clocks.sys_clk.to_Hz()); + + let gpioa = dp.GPIOA.split(&mut rcc); + let gpiob = dp.GPIOB.split(&mut rcc); + + let sda = gpiob.pb7.into_alternate_open_drain(); + let scl = gpioa.pa15.into_alternate_open_drain(); + + let mut syscfg = dp.SYSCFG.constrain(&mut rcc); + + // Enable Fast Mode Plus for I2C1 + syscfg.i2c_fmp_enable::<1>(true); + + // Configure I2C for 1MHz + let i2c = dp.I2C1.i2c(sda, scl, 1.MHz(), &mut rcc); + + let mut as5600 = As5600::new(i2c); + + loop { + match as5600.angle() { + Ok(angle) => { + // Convert angle to degrees + let angle_degrees = angle as f32 * 360.0 / 4096.0; + info!("Angle: {}°", angle_degrees); + } + Err(e) => match e { + as5600::error::Error::Communication(_) => error!("I2C communication error"), + as5600::error::Error::Status(_error) => error!("AS5600 status error"), + as5600::error::Error::Configuration(_error) => error!("AS5600 configuration error"), + _ => error!("Other AS5600 error"), + }, + } + } +} diff --git a/src/syscfg.rs b/src/syscfg.rs index b13833bb..8da543a0 100644 --- a/src/syscfg.rs +++ b/src/syscfg.rs @@ -1,25 +1,17 @@ -use crate::bb; -use crate::stm32::{RCC, SYSCFG}; +use crate::rcc::{Enable, Rcc, Reset}; +use crate::stm32::SYSCFG; use core::ops::Deref; /// Extension trait that constrains the `SYSCFG` peripheral pub trait SysCfgExt { /// Constrains the `SYSCFG` peripheral so it plays nicely with the other abstractions - fn constrain(self) -> SysCfg; + fn constrain(self, rcc: &mut Rcc) -> SysCfg; } impl SysCfgExt for SYSCFG { - fn constrain(self) -> SysCfg { - unsafe { - // NOTE(unsafe) this reference will only be used for atomic writes with no side effects. - let rcc = &(*RCC::ptr()); - - // Enable clock. - bb::set(&rcc.apb2enr(), 0); - - // Stall the pipeline to work around erratum 2.1.13 (DM00037591) - cortex_m::asm::dsb(); - } + fn constrain(self, rcc: &mut Rcc) -> SysCfg { + SYSCFG::enable(rcc); + SYSCFG::reset(rcc); SysCfg(self) } @@ -35,3 +27,22 @@ impl Deref for SysCfg { &self.0 } } + +impl SysCfg { + /// Enable/disable I2C fast mode plus on the I2C bus index provided as a const generic parameter. + /// + /// Pins that are configured as I2C alternate functions will be configured as fast mode plus. + /// The alternate function mode of the pin must be set before FMP is enabled in SysCfg. + /// + /// When FM+ mode is activated on a pin, the GPIO speed configuration (OSPEEDR) is ignored and overridden. + /// + pub fn i2c_fmp_enable(&mut self, en: bool) { + match BUS { + 1 => (*self).cfgr1().modify(|_, w| w.i2c1_fmp().bit(en)), + 2 => (*self).cfgr1().modify(|_, w| w.i2c2_fmp().bit(en)), + 3 => (*self).cfgr1().modify(|_, w| w.i2c3_fmp().bit(en)), + 4 => (*self).cfgr1().modify(|_, w| w.i2c4_fmp().bit(en)), + _ => panic!("Invalid I2C bus"), + }; + } +}