diff --git a/.vscode/settings.json b/.vscode/settings.json index 725fb69d..29e8812e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -25,6 +25,7 @@ // "examples/stm32f1/Cargo.toml", // "examples/stm32f2/Cargo.toml", // "examples/stm32f3/Cargo.toml", + // "examples/stm32f334/Cargo.toml", // "examples/stm32f4/Cargo.toml", // "examples/stm32f7/Cargo.toml", // "examples/stm32g0/Cargo.toml", diff --git a/ci.sh b/ci.sh index 19628b50..32ae7855 100755 --- a/ci.sh +++ b/ci.sh @@ -117,6 +117,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32f1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f1 \ --- build --release --manifest-path examples/stm32f2/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32f2 \ --- build --release --manifest-path examples/stm32f3/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f3 \ + --- build --release --manifest-path examples/stm32f334/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f334 \ --- build --release --manifest-path examples/stm32f4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32f4 \ --- build --release --manifest-path examples/stm32f7/Cargo.toml --target thumbv7em-none-eabihf --out-dir out/examples/stm32f7 \ --- build --release --manifest-path examples/stm32c0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32c0 \ diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs index 409a943d..9b3caefd 100644 --- a/embassy-stm32/build.rs +++ b/embassy-stm32/build.rs @@ -587,6 +587,16 @@ fn main() { (("timer", "BKIN2"), quote!(crate::timer::BreakInput2Pin)), (("timer", "BKIN2_COMP1"), quote!(crate::timer::BreakInput2Comparator1Pin)), (("timer", "BKIN2_COMP2"), quote!(crate::timer::BreakInput2Comparator2Pin)), + (("hrtim", "CHA1"), quote!(crate::hrtim::ChannelAPin)), + (("hrtim", "CHA2"), quote!(crate::hrtim::ChannelAComplementaryPin)), + (("hrtim", "CHB1"), quote!(crate::hrtim::ChannelBPin)), + (("hrtim", "CHB2"), quote!(crate::hrtim::ChannelBComplementaryPin)), + (("hrtim", "CHC1"), quote!(crate::hrtim::ChannelCPin)), + (("hrtim", "CHC2"), quote!(crate::hrtim::ChannelCComplementaryPin)), + (("hrtim", "CHD1"), quote!(crate::hrtim::ChannelDPin)), + (("hrtim", "CHD2"), quote!(crate::hrtim::ChannelDComplementaryPin)), + (("hrtim", "CHE1"), quote!(crate::hrtim::ChannelEPin)), + (("hrtim", "CHE2"), quote!(crate::hrtim::ChannelEComplementaryPin)), (("sdmmc", "CK"), quote!(crate::sdmmc::CkPin)), (("sdmmc", "CMD"), quote!(crate::sdmmc::CmdPin)), (("sdmmc", "D0"), quote!(crate::sdmmc::D0Pin)), diff --git a/embassy-stm32/src/hrtim/mod.rs b/embassy-stm32/src/hrtim/mod.rs new file mode 100644 index 00000000..a930ff73 --- /dev/null +++ b/embassy-stm32/src/hrtim/mod.rs @@ -0,0 +1,409 @@ +mod traits; + +use core::marker::PhantomData; + +use embassy_hal_internal::{into_ref, PeripheralRef}; +pub use traits::Instance; + +#[allow(unused_imports)] +use crate::gpio::sealed::{AFType, Pin}; +use crate::gpio::AnyPin; +use crate::time::Hertz; +use crate::Peripheral; + +pub enum Source { + Master, + ChA, + ChB, + ChC, + ChD, + ChE, +} + +pub struct BurstController { + phantom: PhantomData, +} +pub struct Master { + phantom: PhantomData, +} +pub struct ChA { + phantom: PhantomData, +} +pub struct ChB { + phantom: PhantomData, +} +pub struct ChC { + phantom: PhantomData, +} +pub struct ChD { + phantom: PhantomData, +} +pub struct ChE { + phantom: PhantomData, +} + +mod sealed { + use super::Instance; + + pub trait AdvancedChannel { + fn raw() -> usize; + } +} + +pub trait AdvancedChannel: sealed::AdvancedChannel {} + +pub struct PwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +pub struct ComplementaryPwmPin<'d, Perip, Channel> { + _pin: PeripheralRef<'d, AnyPin>, + phantom: PhantomData<(Perip, Channel)>, +} + +macro_rules! advanced_channel_impl { + ($new_chx:ident, $channel:tt, $ch_num:expr, $pin_trait:ident, $complementary_pin_trait:ident) => { + impl<'d, Perip: Instance> PwmPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + PwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + + impl<'d, Perip: Instance> ComplementaryPwmPin<'d, Perip, $channel> { + pub fn $new_chx(pin: impl Peripheral

> + 'd) -> Self { + into_ref!(pin); + critical_section::with(|_| { + pin.set_low(); + pin.set_as_af(pin.af_num(), AFType::OutputPushPull); + #[cfg(gpio_v2)] + pin.set_speed(crate::gpio::Speed::VeryHigh); + }); + ComplementaryPwmPin { + _pin: pin.map_into(), + phantom: PhantomData, + } + } + } + + impl sealed::AdvancedChannel for $channel { + fn raw() -> usize { + $ch_num + } + } + impl AdvancedChannel for $channel {} + }; +} + +advanced_channel_impl!(new_cha, ChA, 0, ChannelAPin, ChannelAComplementaryPin); +advanced_channel_impl!(new_chb, ChB, 1, ChannelBPin, ChannelBComplementaryPin); +advanced_channel_impl!(new_chc, ChC, 2, ChannelCPin, ChannelCComplementaryPin); +advanced_channel_impl!(new_chd, ChD, 3, ChannelDPin, ChannelDComplementaryPin); +advanced_channel_impl!(new_che, ChE, 4, ChannelEPin, ChannelEComplementaryPin); + +/// Struct used to divide a high resolution timer into multiple channels +pub struct AdvancedPwm<'d, T: Instance> { + _inner: PeripheralRef<'d, T>, + pub master: Master, + pub burst_controller: BurstController, + pub ch_a: ChA, + pub ch_b: ChB, + pub ch_c: ChC, + pub ch_d: ChD, + pub ch_e: ChE, +} + +impl<'d, T: Instance> AdvancedPwm<'d, T> { + pub fn new( + tim: impl Peripheral

+ 'd, + _cha: Option>>, + _chan: Option>>, + _chb: Option>>, + _chbn: Option>>, + _chc: Option>>, + _chcn: Option>>, + _chd: Option>>, + _chdn: Option>>, + _che: Option>>, + _chen: Option>>, + ) -> Self { + Self::new_inner(tim) + } + + fn new_inner(tim: impl Peripheral

+ 'd) -> Self { + into_ref!(tim); + + T::enable(); + ::reset(); + + // // Enable and and stabilize the DLL + // T::regs().dllcr().modify(|w| { + // // w.set_calen(true); + // // w.set_calrte(11); + // w.set_cal(true); + // }); + // + // debug!("wait for dll calibration"); + // while !T::regs().isr().read().dllrdy() {} + // + // debug!("dll calibration complete"); + + Self { + _inner: tim, + master: Master { phantom: PhantomData }, + burst_controller: BurstController { phantom: PhantomData }, + ch_a: ChA { phantom: PhantomData }, + ch_b: ChB { phantom: PhantomData }, + ch_c: ChC { phantom: PhantomData }, + ch_d: ChD { phantom: PhantomData }, + ch_e: ChE { phantom: PhantomData }, + } + } +} + +impl BurstController { + pub fn set_source(&mut self, _source: Source) { + todo!("burst mode control registers not implemented") + } +} + +/// Represents a fixed-frequency bridge converter +/// +/// Our implementation of the bridge converter uses a single channel and three compare registers, +/// allowing implementation of a synchronous buck or boost converter in continuous or discontinuous +/// conduction mode. +/// +/// It is important to remember that in synchronous topologies, energy can flow in reverse during +/// light loading conditions, and that the low-side switch must be active for a short time to drive +/// a bootstrapped high-side switch. +pub struct BridgeConverter> { + timer: PhantomData, + channel: PhantomData, + dead_time: u16, + primary_duty: u16, + min_secondary_duty: u16, + max_secondary_duty: u16, +} + +impl> BridgeConverter { + pub fn new(_channel: C, frequency: Hertz) -> Self { + use crate::pac::hrtim::vals::{Activeeffect, Inactiveeffect}; + + T::set_channel_frequency(C::raw(), frequency); + + // Always enable preload + T::regs().tim(C::raw()).cr().modify(|w| { + w.set_preen(true); + w.set_repu(true); + w.set_cont(true); + }); + + // Enable timer outputs + T::regs().oenr().modify(|w| { + w.set_t1oen(C::raw(), true); + w.set_t2oen(C::raw(), true); + }); + + // The dead-time generation unit cannot be used because it forces the other output + // to be completely complementary to the first output, which restricts certain waveforms + // Therefore, software-implemented dead time must be used when setting the duty cycles + + // Set output 1 to active on a period event + T::regs() + .tim(C::raw()) + .setr(0) + .modify(|w| w.set_per(Activeeffect::SETACTIVE)); + + // Set output 1 to inactive on a compare 1 event + T::regs() + .tim(C::raw()) + .rstr(0) + .modify(|w| w.set_cmp(0, Inactiveeffect::SETINACTIVE)); + + // Set output 2 to active on a compare 2 event + T::regs() + .tim(C::raw()) + .setr(1) + .modify(|w| w.set_cmp(1, Activeeffect::SETACTIVE)); + + // Set output 2 to inactive on a compare 3 event + T::regs() + .tim(C::raw()) + .rstr(1) + .modify(|w| w.set_cmp(2, Inactiveeffect::SETINACTIVE)); + + Self { + timer: PhantomData, + channel: PhantomData, + dead_time: 0, + primary_duty: 0, + min_secondary_duty: 0, + max_secondary_duty: 0, + } + } + + pub fn start(&mut self) { + T::regs().mcr().modify(|w| w.set_tcen(C::raw(), true)); + } + + pub fn stop(&mut self) { + T::regs().mcr().modify(|w| w.set_tcen(C::raw(), false)); + } + + pub fn enable_burst_mode(&mut self) { + T::regs().tim(C::raw()).outr().modify(|w| { + // Enable Burst Mode + w.set_idlem(0, true); + w.set_idlem(1, true); + + // Set output to active during the burst + w.set_idles(0, true); + w.set_idles(1, true); + }) + } + + pub fn disable_burst_mode(&mut self) { + T::regs().tim(C::raw()).outr().modify(|w| { + // Disable Burst Mode + w.set_idlem(0, false); + w.set_idlem(1, false); + }) + } + + fn update_primary_duty_or_dead_time(&mut self) { + self.min_secondary_duty = self.primary_duty + self.dead_time; + + T::regs().tim(C::raw()).cmp(0).modify(|w| w.set_cmp(self.primary_duty)); + T::regs() + .tim(C::raw()) + .cmp(1) + .modify(|w| w.set_cmp(self.min_secondary_duty)); + } + + /// Set the dead time as a proportion of the maximum compare value + pub fn set_dead_time(&mut self, dead_time: u16) { + self.dead_time = dead_time; + self.max_secondary_duty = self.get_max_compare_value() - dead_time; + self.update_primary_duty_or_dead_time(); + } + + /// Get the maximum compare value of a duty cycle + pub fn get_max_compare_value(&mut self) -> u16 { + T::regs().tim(C::raw()).per().read().per() + } + + /// The primary duty is the period in which the primary switch is active + /// + /// In the case of a buck converter, this is the high-side switch + /// In the case of a boost converter, this is the low-side switch + pub fn set_primary_duty(&mut self, primary_duty: u16) { + self.primary_duty = primary_duty; + self.update_primary_duty_or_dead_time(); + } + + /// The secondary duty is the period in any switch is active + /// + /// If less than or equal to the primary duty, the secondary switch will be active for one tick + /// If a fully complementary output is desired, the secondary duty can be set to the max compare + pub fn set_secondary_duty(&mut self, secondary_duty: u16) { + let secondary_duty = if secondary_duty > self.max_secondary_duty { + self.max_secondary_duty + } else if secondary_duty <= self.min_secondary_duty { + self.min_secondary_duty + 1 + } else { + secondary_duty + }; + + T::regs().tim(C::raw()).cmp(2).modify(|w| w.set_cmp(secondary_duty)); + } +} + +/// Represents a variable-frequency resonant converter +/// +/// This implementation of a resonsant converter is appropriate for a half or full bridge, +/// but does not include secondary rectification, which is appropriate for applications +/// with a low-voltage on the secondary side. +pub struct ResonantConverter> { + timer: PhantomData, + channel: PhantomData, + min_period: u16, + max_period: u16, +} + +impl> ResonantConverter { + pub fn new(_channel: C, min_frequency: Hertz, max_frequency: Hertz) -> Self { + T::set_channel_frequency(C::raw(), min_frequency); + + // Always enable preload + T::regs().tim(C::raw()).cr().modify(|w| { + w.set_preen(true); + w.set_repu(true); + + w.set_cont(true); + w.set_half(true); + }); + + // Enable timer outputs + T::regs().oenr().modify(|w| { + w.set_t1oen(C::raw(), true); + w.set_t2oen(C::raw(), true); + }); + + // Dead-time generator can be used in this case because the primary fets + // of a resonant converter are always complementary + T::regs().tim(C::raw()).outr().modify(|w| w.set_dten(true)); + + let max_period = T::regs().tim(C::raw()).per().read().per(); + let min_period = max_period * (min_frequency.0 / max_frequency.0) as u16; + + Self { + timer: PhantomData, + channel: PhantomData, + min_period: min_period, + max_period: max_period, + } + } + + /// Set the dead time as a proportion of the maximum compare value + pub fn set_dead_time(&mut self, value: u16) { + T::set_channel_dead_time(C::raw(), value); + } + + pub fn set_period(&mut self, period: u16) { + assert!(period < self.max_period); + assert!(period > self.min_period); + + T::regs().tim(C::raw()).per().modify(|w| w.set_per(period)); + } + + /// Get the minimum compare value of a duty cycle + pub fn get_min_period(&mut self) -> u16 { + self.min_period + } + + /// Get the maximum compare value of a duty cycle + pub fn get_max_period(&mut self) -> u16 { + self.max_period + } +} + +pin_trait!(ChannelAPin, Instance); +pin_trait!(ChannelAComplementaryPin, Instance); +pin_trait!(ChannelBPin, Instance); +pin_trait!(ChannelBComplementaryPin, Instance); +pin_trait!(ChannelCPin, Instance); +pin_trait!(ChannelCComplementaryPin, Instance); +pin_trait!(ChannelDPin, Instance); +pin_trait!(ChannelDComplementaryPin, Instance); +pin_trait!(ChannelEPin, Instance); +pin_trait!(ChannelEComplementaryPin, Instance); diff --git a/embassy-stm32/src/hrtim/traits.rs b/embassy-stm32/src/hrtim/traits.rs new file mode 100644 index 00000000..158a6886 --- /dev/null +++ b/embassy-stm32/src/hrtim/traits.rs @@ -0,0 +1,193 @@ +use crate::rcc::sealed::RccPeripheral; +use crate::time::Hertz; + +#[derive(Clone, Copy)] +pub(crate) enum Prescaler { + Div1, + Div2, + Div4, + Div8, + Div16, + Div32, + Div64, + Div128, +} + +impl From for u32 { + fn from(val: Prescaler) -> Self { + match val { + Prescaler::Div1 => 1, + Prescaler::Div2 => 2, + Prescaler::Div4 => 4, + Prescaler::Div8 => 8, + Prescaler::Div16 => 16, + Prescaler::Div32 => 32, + Prescaler::Div64 => 64, + Prescaler::Div128 => 128, + } + } +} + +impl From for u8 { + fn from(val: Prescaler) -> Self { + match val { + Prescaler::Div1 => 0b000, + Prescaler::Div2 => 0b001, + Prescaler::Div4 => 0b010, + Prescaler::Div8 => 0b011, + Prescaler::Div16 => 0b100, + Prescaler::Div32 => 0b101, + Prescaler::Div64 => 0b110, + Prescaler::Div128 => 0b111, + } + } +} + +impl From for Prescaler { + fn from(val: u8) -> Self { + match val { + 0b000 => Prescaler::Div1, + 0b001 => Prescaler::Div2, + 0b010 => Prescaler::Div4, + 0b011 => Prescaler::Div8, + 0b100 => Prescaler::Div16, + 0b101 => Prescaler::Div32, + 0b110 => Prescaler::Div64, + 0b111 => Prescaler::Div128, + _ => unreachable!(), + } + } +} + +impl Prescaler { + pub fn compute_min_high_res(val: u32) -> Self { + *[ + Prescaler::Div1, + Prescaler::Div2, + Prescaler::Div4, + Prescaler::Div8, + Prescaler::Div16, + Prescaler::Div32, + Prescaler::Div64, + Prescaler::Div128, + ] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() + } + + pub fn compute_min_low_res(val: u32) -> Self { + *[Prescaler::Div32, Prescaler::Div64, Prescaler::Div128] + .iter() + .skip_while(|psc| >::into(**psc) <= val) + .next() + .unwrap() + } +} + +pub(crate) mod sealed { + use super::*; + + pub trait Instance: RccPeripheral { + fn regs() -> crate::pac::hrtim::Hrtim; + + fn set_master_frequency(frequency: Hertz); + + fn set_channel_frequency(channnel: usize, frequency: Hertz); + + /// Set the dead time as a proportion of max_duty + fn set_channel_dead_time(channnel: usize, dead_time: u16); + + // fn enable_outputs(enable: bool); + // + // fn enable_channel(&mut self, channel: usize, enable: bool); + } +} + +pub trait Instance: sealed::Instance + 'static {} + +foreach_interrupt! { + ($inst:ident, hrtim, HRTIM, MASTER, $irq:ident) => { + impl sealed::Instance for crate::peripherals::$inst { + fn regs() -> crate::pac::hrtim::Hrtim { + crate::pac::$inst + } + + fn set_master_frequency(frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + + let f = frequency.0; + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = if Self::regs().isr().read().dllrdy() { + Prescaler::compute_min_high_res(psc_min) + } else { + Prescaler::compute_min_low_res(psc_min) + }; + + let psc_val: u32 = psc.into(); + let timer_f = 32 * (timer_f / psc_val); + let per: u16 = (timer_f / f) as u16; + + let regs = Self::regs(); + + regs.mcr().modify(|w| w.set_ckpsc(psc.into())); + regs.mper().modify(|w| w.set_mper(per)); + } + + fn set_channel_frequency(channel: usize, frequency: Hertz) { + use crate::rcc::sealed::RccPeripheral; + + let f = frequency.0; + let timer_f = Self::frequency().0; + let psc_min = (timer_f / f) / (u16::MAX as u32 / 32); + let psc = if Self::regs().isr().read().dllrdy() { + Prescaler::compute_min_high_res(psc_min) + } else { + Prescaler::compute_min_low_res(psc_min) + }; + + let psc_val: u32 = psc.into(); + let timer_f = 32 * (timer_f / psc_val); + let per: u16 = (timer_f / f) as u16; + + let regs = Self::regs(); + + regs.tim(channel).cr().modify(|w| w.set_ckpsc(psc.into())); + regs.tim(channel).per().modify(|w| w.set_per(per)); + } + + fn set_channel_dead_time(channel: usize, dead_time: u16) { + + let regs = Self::regs(); + + let channel_psc: Prescaler = regs.tim(channel).cr().read().ckpsc().into(); + let psc_val: u32 = channel_psc.into(); + + + // The dead-time base clock runs 4 times slower than the hrtim base clock + // u9::MAX = 511 + let psc_min = (psc_val * dead_time as u32) / (4 * 511); + let psc = if Self::regs().isr().read().dllrdy() { + Prescaler::compute_min_high_res(psc_min) + } else { + Prescaler::compute_min_low_res(psc_min) + }; + + let dt_psc_val: u32 = psc.into(); + let dt_val = (dt_psc_val * dead_time as u32) / (4 * psc_val); + + regs.tim(channel).dt().modify(|w| { + w.set_dtprsc(psc.into()); + w.set_dtf(dt_val as u16); + w.set_dtr(dt_val as u16); + }); + } + } + + impl Instance for crate::peripherals::$inst { + + } + }; +} diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs index 9e67596b..34220fbf 100644 --- a/embassy-stm32/src/lib.rs +++ b/embassy-stm32/src/lib.rs @@ -26,6 +26,8 @@ pub mod timer; pub mod adc; #[cfg(can)] pub mod can; +#[cfg(crc)] +pub mod crc; #[cfg(dac)] pub mod dac; #[cfg(dcmi)] @@ -34,14 +36,13 @@ pub mod dcmi; pub mod eth; #[cfg(feature = "exti")] pub mod exti; +pub mod flash; #[cfg(fmc)] pub mod fmc; +#[cfg(hrtim_v1)] +pub mod hrtim; #[cfg(i2c)] pub mod i2c; - -#[cfg(crc)] -pub mod crc; -pub mod flash; #[cfg(all(spi_v1, rcc_f4))] pub mod i2s; #[cfg(stm32wb)] diff --git a/embassy-stm32/src/rcc/f3.rs b/embassy-stm32/src/rcc/f3.rs index 2deee80d..321270a7 100644 --- a/embassy-stm32/src/rcc/f3.rs +++ b/embassy-stm32/src/rcc/f3.rs @@ -264,6 +264,7 @@ fn calc_pll(config: &Config, Hertz(sysclk): Hertz) -> (Hertz, PllConfig) { } #[inline] +#[allow(unused_variables)] fn get_usb_pre(config: &Config, sysclk: u32, pclk1: u32, pll_config: &Option) -> Usbpre { cfg_if::cfg_if! { // Some chips do not have USB diff --git a/examples/stm32f334/.cargo/config.toml b/examples/stm32f334/.cargo/config.toml new file mode 100644 index 00000000..caf947be --- /dev/null +++ b/examples/stm32f334/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace STM32F429ZITx with your chip as listed in `probe-rs-cli chip list` +runner = "probe-run --chip STM32F334R8" + +[build] +target = "thumbv7em-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/stm32f334/Cargo.toml b/examples/stm32f334/Cargo.toml new file mode 100644 index 00000000..6410891a --- /dev/null +++ b/examples/stm32f334/Cargo.toml @@ -0,0 +1,26 @@ +[package] +edition = "2021" +name = "embassy-stm32f3-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[dependencies] +embassy-sync = { version = "0.2.0", path = "../../embassy-sync", features = ["defmt"] } +embassy-executor = { version = "0.2.0", path = "../../embassy-executor", features = ["nightly", "arch-cortex-m", "executor-thread", "executor-interrupt", "defmt", "integrated-timers"] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "stm32f334r8", "unstable-pac", "memory-x", "time-driver-any", "exti"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = ["defmt"] } +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } + +defmt = "0.3" +defmt-rtt = "0.4" + +cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } +cortex-m-rt = "0.7.0" +embedded-hal = "0.2.6" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = ["async-await"] } +heapless = { version = "0.7.5", default-features = false } +nb = "1.0.0" +embedded-storage = "0.3.0" +static_cell = { version = "1.1", features = ["nightly"]} diff --git a/examples/stm32f334/build.rs b/examples/stm32f334/build.rs new file mode 100644 index 00000000..8cd32d7e --- /dev/null +++ b/examples/stm32f334/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/examples/stm32f334/src/bin/button.rs b/examples/stm32f334/src/bin/button.rs new file mode 100644 index 00000000..599c0f27 --- /dev/null +++ b/examples/stm32f334/src/bin/button.rs @@ -0,0 +1,27 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let mut out1 = Output::new(p.PA8, Level::Low, Speed::High); + + out1.set_high(); + Timer::after(Duration::from_millis(500)).await; + out1.set_low(); + + Timer::after(Duration::from_millis(500)).await; + info!("end program"); + + cortex_m::asm::bkpt(); +} diff --git a/examples/stm32f334/src/bin/hello.rs b/examples/stm32f334/src/bin/hello.rs new file mode 100644 index 00000000..65773210 --- /dev/null +++ b/examples/stm32f334/src/bin/hello.rs @@ -0,0 +1,23 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_stm32::time::Hertz; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) -> ! { + let mut config = Config::default(); + config.rcc.hse = Some(Hertz(8_000_000)); + config.rcc.sysclk = Some(Hertz(16_000_000)); + let _p = embassy_stm32::init(config); + + loop { + info!("Hello World!"); + Timer::after(Duration::from_secs(1)).await; + } +} diff --git a/examples/stm32f334/src/bin/pwm.rs b/examples/stm32f334/src/bin/pwm.rs new file mode 100644 index 00000000..2660b10c --- /dev/null +++ b/examples/stm32f334/src/bin/pwm.rs @@ -0,0 +1,71 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::hrtim::*; +use embassy_stm32::time::{khz, mhz}; +use embassy_stm32::Config; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config: Config = Default::default(); + config.rcc.sysclk = Some(mhz(64)); + config.rcc.hclk = Some(mhz(64)); + config.rcc.pclk1 = Some(mhz(32)); + config.rcc.pclk2 = Some(mhz(64)); + + let p = embassy_stm32::init(config); + info!("Hello World!"); + + let ch1 = PwmPin::new_cha(p.PA8); + let ch1n = ComplementaryPwmPin::new_cha(p.PA9); + let pwm = AdvancedPwm::new( + p.HRTIM1, + Some(ch1), + Some(ch1n), + None, + None, + None, + None, + None, + None, + None, + None, + ); + + info!("pwm constructed"); + + let mut buck_converter = BridgeConverter::new(pwm.ch_a, khz(5)); + + // embassy_stm32::pac::HRTIM1 + // .tim(0) + // .setr(0) + // .modify(|w| w.set_sst(Activeeffect::SETACTIVE)); + // + // Timer::after(Duration::from_millis(500)).await; + // + // embassy_stm32::pac::HRTIM1 + // .tim(0) + // .rstr(0) + // .modify(|w| w.set_srt(Inactiveeffect::SETINACTIVE)); + + let max_duty = buck_converter.get_max_compare_value(); + + info!("max compare value: {}", max_duty); + + buck_converter.set_dead_time(max_duty / 20); + buck_converter.set_primary_duty(max_duty / 2); + buck_converter.set_secondary_duty(3 * max_duty / 4); + + buck_converter.start(); + + Timer::after(Duration::from_millis(500)).await; + + info!("end program"); + + cortex_m::asm::bkpt(); +}