diff --git a/ci.sh b/ci.sh index 514c6769b..e95714b65 100755 --- a/ci.sh +++ b/ci.sh @@ -189,6 +189,7 @@ cargo batch \ --- build --release --manifest-path examples/stm32l1/Cargo.toml --target thumbv7m-none-eabi --out-dir out/examples/stm32l1 \ --- build --release --manifest-path examples/stm32l4/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32l4 \ --- build --release --manifest-path examples/stm32l5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32l5 \ + --- build --release --manifest-path examples/stm32u0/Cargo.toml --target thumbv6m-none-eabi --out-dir out/examples/stm32u0 \ --- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32u5 \ --- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --out-dir out/examples/stm32wb \ --- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --out-dir out/examples/stm32wba \ diff --git a/docs/modules/ROOT/pages/faq.adoc b/docs/modules/ROOT/pages/faq.adoc index c6b893de5..0944127a5 100644 --- a/docs/modules/ROOT/pages/faq.adoc +++ b/docs/modules/ROOT/pages/faq.adoc @@ -231,3 +231,34 @@ Please refer to the STM32 documentation for the specific values suitable for you Look for the `MEMORY` section and try to determine the FLASH and RAM sizes and section start. If you find a case where the memory.x is wrong, please report it on [this Github issue](https://github.com/embassy-rs/stm32-data/issues/301) so other users are not caught by surprise. + +== Known issues (details and/or mitigations) + +These are issues that are commonly reported. Help wanted fixing them, or improving the UX when possible! + +=== STM32H5 and STM32H7 power issues + +STM32 chips with built-in power management (SMPS and LDO) settings often cause user problems when the configuration does not match how the board was designed. + +Settings from the examples, or even from other working boards, may not work on YOUR board, because they are wired differently. + +Additionally, some PWR settings require a full device reboot (and enough time to discharge any power capacitors!), making this hard to troubleshoot. Also, some +"wrong" power settings will ALMOST work, meaning it will sometimes work on some boots, or for a while, but crash unexpectedly. + +There is not a fix for this yet, as it is board/hardware dependant. See link:https://github.com/embassy-rs/embassy/issues/2806[this tracking issue] for more details + +=== STM32 BDMA only work out of some RAM regions + +The STM32 BDMA controller included in some chips (TODO: list which ones) has a limitation in that it only works out of certain regions of RAM (TODO: list which ones), otherwise the transfer +will fail. + +If you see errors that look like this: + +[source,plain] +---- +DMA: error on BDMA@1234ABCD channel 4 +---- + +You likely need to set up your linker script to define a special region for this area, and copy data to that region before using with BDMA. + +TODO: show how to do that diff --git a/docs/modules/ROOT/pages/layer_by_layer.adoc b/docs/modules/ROOT/pages/layer_by_layer.adoc index 1d7bdc89b..fa419f75e 100644 --- a/docs/modules/ROOT/pages/layer_by_layer.adoc +++ b/docs/modules/ROOT/pages/layer_by_layer.adoc @@ -63,7 +63,7 @@ Luckily, there is an elegant solution to this problem when using Embassy. == Async version -It's time to use the Embassy capabilities to its fullest. At the core, Embassy has an async excecutor, or a runtime for async tasks if you will. The executor polls a set of tasks (defined at compile time), and whenever a task `blocks`, the executor will run another task, or put the microcontroller to sleep. +It's time to use the Embassy capabilities to its fullest. At the core, Embassy has an async executor, or a runtime for async tasks if you will. The executor polls a set of tasks (defined at compile time), and whenever a task `blocks`, the executor will run another task, or put the microcontroller to sleep. [source,rust] ---- diff --git a/embassy-nrf/src/pdm.rs b/embassy-nrf/src/pdm.rs index ef2662c85..c1501e02e 100644 --- a/embassy-nrf/src/pdm.rs +++ b/embassy-nrf/src/pdm.rs @@ -1,4 +1,4 @@ -//! Pulse Density Modulation (PDM) mirophone driver +//! Pulse Density Modulation (PDM) microphone driver #![macro_use] diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml index 1f9a51678..095427171 100644 --- a/embassy-stm32/Cargo.toml +++ b/embassy-stm32/Cargo.toml @@ -71,7 +71,7 @@ rand_core = "0.6.3" sdio-host = "0.5.0" critical-section = "1.1" #stm32-metapac = { version = "15" } -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d674277b78ca7400ecfeeb1b5af4e460a65c1a61" } +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-01ac9bfd035961dc75f32dcd6080501538246d5c" } vcell = "0.1.3" nb = "1.0.0" @@ -97,7 +97,7 @@ proc-macro2 = "1.0.36" quote = "1.0.15" #stm32-metapac = { version = "15", default-features = false, features = ["metadata"]} -stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-d674277b78ca7400ecfeeb1b5af4e460a65c1a61", default-features = false, features = ["metadata"]} +stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-01ac9bfd035961dc75f32dcd6080501538246d5c", default-features = false, features = ["metadata"]} [features] default = ["rt"] diff --git a/embassy-stm32/src/adc/g4.rs b/embassy-stm32/src/adc/g4.rs index f6741f019..221cc2a40 100644 --- a/embassy-stm32/src/adc/g4.rs +++ b/embassy-stm32/src/adc/g4.rs @@ -170,7 +170,7 @@ impl<'d, T: Instance> Adc<'d, T> { fn configure_differential_inputs(&mut self) { T::regs().difsel().modify(|w| { - for n in 0..20 { + for n in 0..18 { w.set_difsel(n, Difsel::SINGLEENDED); } }); diff --git a/embassy-stm32/src/adc/mod.rs b/embassy-stm32/src/adc/mod.rs index 12c5751bd..8ef68490b 100644 --- a/embassy-stm32/src/adc/mod.rs +++ b/embassy-stm32/src/adc/mod.rs @@ -10,7 +10,7 @@ #[cfg_attr(adc_v1, path = "v1.rs")] #[cfg_attr(adc_l0, path = "v1.rs")] #[cfg_attr(adc_v2, path = "v2.rs")] -#[cfg_attr(any(adc_v3, adc_g0, adc_h5), path = "v3.rs")] +#[cfg_attr(any(adc_v3, adc_g0, adc_h5, adc_u0), path = "v3.rs")] #[cfg_attr(adc_v4, path = "v4.rs")] #[cfg_attr(adc_g4, path = "g4.rs")] mod _version; @@ -76,7 +76,12 @@ pub(crate) fn blocking_delay_us(us: u32) { #[cfg(time)] embassy_time::block_for(embassy_time::Duration::from_micros(us)); #[cfg(not(time))] - cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 * us / 1_000_000); + { + let freq = unsafe { crate::rcc::get_freqs() }.sys.unwrap().0 as u64; + let us = us as u64; + let cycles = freq * us / 1_000_000; + cortex_m::asm::delay(cycles as u32); + } } /// ADC instance. @@ -91,6 +96,7 @@ pub(crate) fn blocking_delay_us(us: u32) { adc_f3, adc_f3_v1_1, adc_g0, + adc_u0, adc_h5 )))] #[allow(private_bounds)] @@ -109,6 +115,7 @@ pub trait Instance: SealedInstance + crate::Peripheral

{ adc_f3, adc_f3_v1_1, adc_g0, + adc_u0, adc_h5 ))] #[allow(private_bounds)] diff --git a/embassy-stm32/src/adc/v3.rs b/embassy-stm32/src/adc/v3.rs index 4fd8558ba..dc418297e 100644 --- a/embassy-stm32/src/adc/v3.rs +++ b/embassy-stm32/src/adc/v3.rs @@ -19,6 +19,8 @@ impl super::SealedAdcPin for VrefInt { let val = 13; } else if #[cfg(adc_h5)] { let val = 17; + } else if #[cfg(adc_u0)] { + let val = 12; } else { let val = 0; } @@ -36,6 +38,8 @@ impl super::SealedAdcPin for Temperature { let val = 12; } else if #[cfg(adc_h5)] { let val = 16; + } else if #[cfg(adc_u0)] { + let val = 11; } else { let val = 17; } @@ -53,6 +57,8 @@ impl super::SealedAdcPin for Vbat { let val = 14; } else if #[cfg(adc_h5)] { let val = 2; + } else if #[cfg(adc_h5)] { + let val = 13; } else { let val = 18; } @@ -73,17 +79,29 @@ cfg_if! { } } +cfg_if! { + if #[cfg(adc_u0)] { + pub struct DacOut; + impl AdcPin for DacOut {} + impl super::SealedAdcPin for DacOut { + fn channel(&self) -> u8 { + 19 + } + } + } +} + impl<'d, T: Instance> Adc<'d, T> { pub fn new(adc: impl Peripheral

+ 'd) -> Self { into_ref!(adc); T::enable_and_reset(); T::regs().cr().modify(|reg| { - #[cfg(not(adc_g0))] + #[cfg(not(any(adc_g0, adc_u0)))] reg.set_deeppwd(false); reg.set_advregen(true); }); - #[cfg(adc_g0)] + #[cfg(any(adc_g0, adc_u0))] T::regs().cfgr1().modify(|reg| { reg.set_chselrmod(false); }); @@ -107,11 +125,11 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn enable_vrefint(&self) -> VrefInt { - #[cfg(not(adc_g0))] + #[cfg(not(any(adc_g0, adc_u0)))] T::common_regs().ccr().modify(|reg| { reg.set_vrefen(true); }); - #[cfg(adc_g0)] + #[cfg(any(adc_g0, adc_u0))] T::regs().ccr().modify(|reg| { reg.set_vrefen(true); }); @@ -125,7 +143,7 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn enable_temperature(&self) -> Temperature { cfg_if! { - if #[cfg(adc_g0)] { + if #[cfg(any(adc_g0, adc_u0))] { T::regs().ccr().modify(|reg| { reg.set_tsen(true); }); @@ -145,7 +163,7 @@ impl<'d, T: Instance> Adc<'d, T> { pub fn enable_vbat(&self) -> Vbat { cfg_if! { - if #[cfg(adc_g0)] { + if #[cfg(any(adc_g0, adc_u0))] { T::regs().ccr().modify(|reg| { reg.set_vbaten(true); }); @@ -168,9 +186,9 @@ impl<'d, T: Instance> Adc<'d, T> { } pub fn set_resolution(&mut self, resolution: Resolution) { - #[cfg(not(adc_g0))] + #[cfg(not(any(adc_g0, adc_u0)))] T::regs().cfgr().modify(|reg| reg.set_res(resolution.into())); - #[cfg(adc_g0)] + #[cfg(any(adc_g0, adc_u0))] T::regs().cfgr1().modify(|reg| reg.set_res(resolution.into())); } @@ -231,9 +249,9 @@ impl<'d, T: Instance> Adc<'d, T> { Self::set_channel_sample_time(pin.channel(), self.sample_time); // Select channel - #[cfg(not(adc_g0))] + #[cfg(not(any(adc_g0, adc_u0)))] T::regs().sqr1().write(|reg| reg.set_sq(0, pin.channel())); - #[cfg(adc_g0)] + #[cfg(any(adc_g0, adc_u0))] T::regs().chselr().write(|reg| reg.set_chsel(1 << pin.channel())); // Some models are affected by an erratum: @@ -261,7 +279,7 @@ impl<'d, T: Instance> Adc<'d, T> { fn set_channel_sample_time(_ch: u8, sample_time: SampleTime) { cfg_if! { - if #[cfg(adc_g0)] { + if #[cfg(any(adc_g0, adc_u0))] { T::regs().smpr().modify(|reg| reg.set_smp1(sample_time.into())); } else if #[cfg(adc_h5)] { match _ch { diff --git a/embassy-stm32/src/can/fd/peripheral.rs b/embassy-stm32/src/can/fd/peripheral.rs index e32f19d91..e5cfee528 100644 --- a/embassy-stm32/src/can/fd/peripheral.rs +++ b/embassy-stm32/src/can/fd/peripheral.rs @@ -368,6 +368,7 @@ impl Registers { w.set_rfne(0, true); // Rx Fifo 0 New Msg w.set_rfne(1, true); // Rx Fifo 1 New Msg w.set_tce(true); // Tx Complete + w.set_boe(true); // Bus-Off Status Changed }); self.regs.ile().modify(|w| { w.set_eint0(true); // Interrupt Line 0 diff --git a/embassy-stm32/src/can/fdcan.rs b/embassy-stm32/src/can/fdcan.rs index e31821ca2..563f542d4 100644 --- a/embassy-stm32/src/can/fdcan.rs +++ b/embassy-stm32/src/can/fdcan.rs @@ -44,53 +44,51 @@ impl interrupt::typelevel::Handler for IT0Interrup let ir = regs.ir().read(); - { - if ir.tc() { - regs.ir().write(|w| w.set_tc(true)); - } - if ir.tefn() { - regs.ir().write(|w| w.set_tefn(true)); - } - - match &T::state().tx_mode { - TxMode::NonBuffered(waker) => waker.wake(), - TxMode::ClassicBuffered(buf) => { - if !T::registers().tx_queue_is_full() { - match buf.tx_receiver.try_receive() { - Ok(frame) => { - _ = T::registers().write(&frame); - } - Err(_) => {} - } - } - } - TxMode::FdBuffered(buf) => { - if !T::registers().tx_queue_is_full() { - match buf.tx_receiver.try_receive() { - Ok(frame) => { - _ = T::registers().write(&frame); - } - Err(_) => {} - } - } - } - } + if ir.tc() { + regs.ir().write(|w| w.set_tc(true)); + } + if ir.tefn() { + regs.ir().write(|w| w.set_tefn(true)); } - if ir.ped() || ir.pea() { - regs.ir().write(|w| { - w.set_ped(true); - w.set_pea(true); - }); + match &T::state().tx_mode { + TxMode::NonBuffered(waker) => waker.wake(), + TxMode::ClassicBuffered(buf) => { + if !T::registers().tx_queue_is_full() { + match buf.tx_receiver.try_receive() { + Ok(frame) => { + _ = T::registers().write(&frame); + } + Err(_) => {} + } + } + } + TxMode::FdBuffered(buf) => { + if !T::registers().tx_queue_is_full() { + match buf.tx_receiver.try_receive() { + Ok(frame) => { + _ = T::registers().write(&frame); + } + Err(_) => {} + } + } + } } if ir.rfn(0) { T::state().rx_mode.on_interrupt::(0); } - if ir.rfn(1) { T::state().rx_mode.on_interrupt::(1); } + + if ir.bo() { + regs.ir().write(|w| w.set_bo(true)); + if regs.psr().read().bo() { + // Initiate bus-off recovery sequence by resetting CCCR.INIT + regs.cccr().modify(|w| w.set_init(false)); + } + } } } diff --git a/embassy-stm32/src/crc/v2v3.rs b/embassy-stm32/src/crc/v2v3.rs index 13fb6778c..ad7c79f12 100644 --- a/embassy-stm32/src/crc/v2v3.rs +++ b/embassy-stm32/src/crc/v2v3.rs @@ -13,6 +13,8 @@ pub struct Crc<'d> { } /// CRC configuration errlr +#[derive(Debug)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] pub enum ConfigError { /// The selected polynomial is invalid. InvalidPolynomial, diff --git a/embassy-stm32/src/dac/tsel.rs b/embassy-stm32/src/dac/tsel.rs index 22d8d3dfa..1877954b9 100644 --- a/embassy-stm32/src/dac/tsel.rs +++ b/embassy-stm32/src/dac/tsel.rs @@ -235,6 +235,23 @@ pub enum TriggerSel { Exti9 = 13, } +/// Trigger selection for U0. +#[cfg(stm32u0)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum TriggerSel { + Software = 0, + Tim1 = 1, + Tim2 = 2, + Tim3 = 3, + Tim6 = 5, + Tim7 = 6, + Tim15 = 8, + Lptim1 = 11, + Lptim2 = 12, + Exti9 = 14, +} + /// Trigger selection for G4. #[cfg(stm32g4)] #[derive(Debug, Copy, Clone, Eq, PartialEq)] diff --git a/embassy-stm32/src/flash/mod.rs b/embassy-stm32/src/flash/mod.rs index 9d7861816..8c6ca2471 100644 --- a/embassy-stm32/src/flash/mod.rs +++ b/embassy-stm32/src/flash/mod.rs @@ -101,10 +101,11 @@ pub enum FlashBank { #[cfg_attr(flash_h7ab, path = "h7.rs")] #[cfg_attr(flash_u5, path = "u5.rs")] #[cfg_attr(flash_h50, path = "h50.rs")] +#[cfg_attr(flash_u0, path = "u0.rs")] #[cfg_attr( not(any( flash_l0, flash_l1, flash_l4, flash_wl, flash_wb, flash_f0, flash_f1, flash_f3, flash_f4, flash_f7, flash_g0, - flash_g4, flash_h7, flash_h7ab, flash_u5, flash_h50 + flash_g4, flash_h7, flash_h7ab, flash_u5, flash_h50, flash_u0 )), path = "other.rs" )] diff --git a/embassy-stm32/src/flash/u0.rs b/embassy-stm32/src/flash/u0.rs new file mode 100644 index 000000000..dfc5a2f76 --- /dev/null +++ b/embassy-stm32/src/flash/u0.rs @@ -0,0 +1,96 @@ +use core::ptr::write_volatile; +use core::sync::atomic::{fence, Ordering}; + +use cortex_m::interrupt; + +use super::{FlashRegion, FlashSector, FLASH_REGIONS, WRITE_SIZE}; +use crate::flash::Error; +use crate::pac; + +pub(crate) const fn is_default_layout() -> bool { + true +} + +pub(crate) const fn get_flash_regions() -> &'static [&'static FlashRegion] { + &FLASH_REGIONS +} + +pub(crate) unsafe fn lock() { + pac::FLASH.cr().modify(|w| w.set_lock(true)); +} +pub(crate) unsafe fn unlock() { + // Wait, while the memory interface is busy. + while pac::FLASH.sr().read().bsy1() {} + + // Unlock flash + if pac::FLASH.cr().read().lock() { + pac::FLASH.keyr().write(|w| w.set_key(0x4567_0123)); + pac::FLASH.keyr().write(|w| w.set_key(0xCDEF_89AB)); + } +} + +pub(crate) unsafe fn enable_blocking_write() { + assert_eq!(0, WRITE_SIZE % 4); + pac::FLASH.cr().write(|w| w.set_pg(true)); +} + +pub(crate) unsafe fn disable_blocking_write() { + pac::FLASH.cr().write(|w| w.set_pg(false)); +} + +pub(crate) unsafe fn blocking_write(start_address: u32, buf: &[u8; WRITE_SIZE]) -> Result<(), Error> { + let mut address = start_address; + for val in buf.chunks(4) { + write_volatile(address as *mut u32, u32::from_le_bytes(val.try_into().unwrap())); + address += val.len() as u32; + + // prevents parallelism errors + fence(Ordering::SeqCst); + } + + wait_ready_blocking() +} + +pub(crate) unsafe fn blocking_erase_sector(sector: &FlashSector) -> Result<(), Error> { + let idx = (sector.start - super::FLASH_BASE as u32) / super::BANK1_REGION.erase_size as u32; + while pac::FLASH.sr().read().bsy1() {} + clear_all_err(); + + interrupt::free(|_| { + pac::FLASH.cr().modify(|w| { + w.set_per(true); + w.set_pnb(idx as u8); + w.set_strt(true); + }); + }); + + let ret: Result<(), Error> = wait_ready_blocking(); + pac::FLASH.cr().modify(|w| w.set_per(false)); + ret +} + +pub(crate) unsafe fn wait_ready_blocking() -> Result<(), Error> { + while pac::FLASH.sr().read().bsy1() {} + + let sr = pac::FLASH.sr().read(); + + if sr.progerr() { + return Err(Error::Prog); + } + + if sr.wrperr() { + return Err(Error::Protected); + } + + if sr.pgaerr() { + return Err(Error::Unaligned); + } + + Ok(()) +} + +pub(crate) unsafe fn clear_all_err() { + // read and write back the same value. + // This clears all "write 1 to clear" bits. + pac::FLASH.sr().modify(|_| {}); +} diff --git a/embassy-stm32/src/usart/mod.rs b/embassy-stm32/src/usart/mod.rs index e7fdf4da6..d21e5c47c 100644 --- a/embassy-stm32/src/usart/mod.rs +++ b/embassy-stm32/src/usart/mod.rs @@ -933,7 +933,6 @@ impl<'d, T: BasicInstance> Uart<'d, T, Async> { /// I/O in idle or in reception. /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict /// on the line must be managed by software (for instance by using a centralized arbiter). - #[cfg(not(any(usart_v1, usart_v2)))] #[doc(alias("HDSEL"))] pub fn new_half_duplex( peri: impl Peripheral

+ 'd, @@ -943,7 +942,10 @@ impl<'d, T: BasicInstance> Uart<'d, T, Async> { rx_dma: impl Peripheral

> + 'd, mut config: Config, ) -> Result { - config.swap_rx_tx = false; + #[cfg(not(any(usart_v1, usart_v2)))] + { + config.swap_rx_tx = false; + } config.half_duplex = true; Self::new_inner( @@ -1084,14 +1086,16 @@ impl<'d, T: BasicInstance> Uart<'d, T, Blocking> { /// I/O in idle or in reception. /// Apart from this, the communication protocol is similar to normal USART mode. Any conflict /// on the line must be managed by software (for instance by using a centralized arbiter). - #[cfg(not(any(usart_v1, usart_v2)))] #[doc(alias("HDSEL"))] pub fn new_blocking_half_duplex( peri: impl Peripheral

+ 'd, tx: impl Peripheral

> + 'd, mut config: Config, ) -> Result { - config.swap_rx_tx = false; + #[cfg(not(any(usart_v1, usart_v2)))] + { + config.swap_rx_tx = false; + } config.half_duplex = true; Self::new_inner( @@ -1354,10 +1358,9 @@ fn configure( } }); - #[cfg(not(usart_v1))] r.cr3().modify(|w| { + #[cfg(not(usart_v1))] w.set_onebit(config.assume_noise_free); - #[cfg(any(usart_v3, usart_v4))] w.set_hdsel(config.half_duplex); }); diff --git a/embassy-stm32/src/usb/mod.rs b/embassy-stm32/src/usb/mod.rs index 1e3c44167..349438ec5 100644 --- a/embassy-stm32/src/usb/mod.rs +++ b/embassy-stm32/src/usb/mod.rs @@ -23,7 +23,7 @@ fn common_init() { ) } - #[cfg(any(stm32l4, stm32l5, stm32wb))] + #[cfg(any(stm32l4, stm32l5, stm32wb, stm32u0))] critical_section::with(|_| crate::pac::PWR.cr2().modify(|w| w.set_usv(true))); #[cfg(pwr_h5)] diff --git a/embassy-stm32/src/usb/otg.rs b/embassy-stm32/src/usb/otg.rs index cabc06367..b386c6977 100644 --- a/embassy-stm32/src/usb/otg.rs +++ b/embassy-stm32/src/usb/otg.rs @@ -672,45 +672,51 @@ impl<'d, T: Instance> Bus<'d, T> { let r = T::regs(); - // Configure RX fifo size. All endpoints share the same FIFO area. - let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out); - trace!("configuring rx fifo size={}", rx_fifo_size_words); + // ERRATA NOTE: Don't interrupt FIFOs being written to. The interrupt + // handler COULD interrupt us here and do FIFO operations, so ensure + // the interrupt does not occur. + critical_section::with(|_| { + // Configure RX fifo size. All endpoints share the same FIFO area. + let rx_fifo_size_words = RX_FIFO_EXTRA_SIZE_WORDS + ep_fifo_size(&self.ep_out); + trace!("configuring rx fifo size={}", rx_fifo_size_words); - r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)); + r.grxfsiz().modify(|w| w.set_rxfd(rx_fifo_size_words)); - // Configure TX (USB in direction) fifo size for each endpoint - let mut fifo_top = rx_fifo_size_words; - for i in 0..T::ENDPOINT_COUNT { - if let Some(ep) = self.ep_in[i] { - trace!( - "configuring tx fifo ep={}, offset={}, size={}", - i, - fifo_top, - ep.fifo_size_words - ); + // Configure TX (USB in direction) fifo size for each endpoint + let mut fifo_top = rx_fifo_size_words; + for i in 0..T::ENDPOINT_COUNT { + if let Some(ep) = self.ep_in[i] { + trace!( + "configuring tx fifo ep={}, offset={}, size={}", + i, + fifo_top, + ep.fifo_size_words + ); - let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) }; + let dieptxf = if i == 0 { r.dieptxf0() } else { r.dieptxf(i - 1) }; - dieptxf.write(|w| { - w.set_fd(ep.fifo_size_words); - w.set_sa(fifo_top); - }); + dieptxf.write(|w| { + w.set_fd(ep.fifo_size_words); + w.set_sa(fifo_top); + }); - fifo_top += ep.fifo_size_words; + fifo_top += ep.fifo_size_words; + } } - } - assert!( - fifo_top <= T::FIFO_DEPTH_WORDS, - "FIFO allocations exceeded maximum capacity" - ); + assert!( + fifo_top <= T::FIFO_DEPTH_WORDS, + "FIFO allocations exceeded maximum capacity" + ); - // Flush fifos - r.grstctl().write(|w| { - w.set_rxfflsh(true); - w.set_txfflsh(true); - w.set_txfnum(0x10); + // Flush fifos + r.grstctl().write(|w| { + w.set_rxfflsh(true); + w.set_txfflsh(true); + w.set_txfnum(0x10); + }); }); + loop { let x = r.grstctl().read(); if !x.rxfflsh() && !x.txfflsh() { @@ -1208,27 +1214,31 @@ impl<'d, T: Instance> embassy_usb_driver::EndpointIn for Endpoint<'d, T, In> { .await } - // Setup transfer size - r.dieptsiz(index).write(|w| { - w.set_mcnt(1); - w.set_pktcnt(1); - w.set_xfrsiz(buf.len() as _); - }); - + // ERRATA: Transmit data FIFO is corrupted when a write sequence to the FIFO is interrupted with + // accesses to certain OTG_FS registers. + // + // Prevent the interrupt (which might poke FIFOs) from executing while copying data to FIFOs. critical_section::with(|_| { + // Setup transfer size + r.dieptsiz(index).write(|w| { + w.set_mcnt(1); + w.set_pktcnt(1); + w.set_xfrsiz(buf.len() as _); + }); + // Enable endpoint r.diepctl(index).modify(|w| { w.set_cnak(true); w.set_epena(true); }); - }); - // Write data to FIFO - for chunk in buf.chunks(4) { - let mut tmp = [0u8; 4]; - tmp[0..chunk.len()].copy_from_slice(chunk); - r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))); - } + // Write data to FIFO + for chunk in buf.chunks(4) { + let mut tmp = [0u8; 4]; + tmp[0..chunk.len()].copy_from_slice(chunk); + r.fifo(index).write_value(regs::Fifo(u32::from_ne_bytes(tmp))); + } + }); trace!("write done ep={:?}", self.info.addr); diff --git a/embassy-stm32/src/usb/usb.rs b/embassy-stm32/src/usb/usb.rs index f48808cb3..81a2d2623 100644 --- a/embassy-stm32/src/usb/usb.rs +++ b/embassy-stm32/src/usb/usb.rs @@ -107,14 +107,14 @@ const EP_COUNT: usize = 8; #[cfg(any(usbram_16x1_512, usbram_16x2_512))] const USBRAM_SIZE: usize = 512; -#[cfg(usbram_16x2_1024)] +#[cfg(any(usbram_16x2_1024, usbram_32_1024))] const USBRAM_SIZE: usize = 1024; #[cfg(usbram_32_2048)] const USBRAM_SIZE: usize = 2048; -#[cfg(not(usbram_32_2048))] +#[cfg(not(any(usbram_32_2048, usbram_32_1024)))] const USBRAM_ALIGN: usize = 2; -#[cfg(usbram_32_2048)] +#[cfg(any(usbram_32_2048, usbram_32_1024))] const USBRAM_ALIGN: usize = 4; const NEW_AW: AtomicWaker = AtomicWaker::new(); @@ -159,7 +159,7 @@ fn calc_out_len(len: u16) -> (u16, u16) { } } -#[cfg(not(usbram_32_2048))] +#[cfg(not(any(usbram_32_2048, usbram_32_1024)))] mod btable { use super::*; @@ -180,7 +180,7 @@ mod btable { USBRAM.mem(index * 4 + 3).read() } } -#[cfg(usbram_32_2048)] +#[cfg(any(usbram_32_2048, usbram_32_1024))] mod btable { use super::*; @@ -224,9 +224,9 @@ impl EndpointBuffer { let n = USBRAM_ALIGN.min(buf.len() - i * USBRAM_ALIGN); val[..n].copy_from_slice(&buf[i * USBRAM_ALIGN..][..n]); - #[cfg(not(usbram_32_2048))] + #[cfg(not(any(usbram_32_2048, usbram_32_1024)))] let val = u16::from_le_bytes(val); - #[cfg(usbram_32_2048)] + #[cfg(any(usbram_32_2048, usbram_32_1024))] let val = u32::from_le_bytes(val); USBRAM.mem(self.addr as usize / USBRAM_ALIGN + i).write_value(val); } diff --git a/examples/stm32f334/.cargo/config.toml b/examples/stm32f334/.cargo/config.toml index caf947be6..f38c90a31 100644 --- a/examples/stm32f334/.cargo/config.toml +++ b/examples/stm32f334/.cargo/config.toml @@ -1,6 +1,6 @@ [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" +runner = "probe-rs run --chip STM32F334R8" [build] target = "thumbv7em-none-eabihf" diff --git a/examples/stm32l4/.cargo/config.toml b/examples/stm32l4/.cargo/config.toml index db3a7ceff..83fc6d6f8 100644 --- a/examples/stm32l4/.cargo/config.toml +++ b/examples/stm32l4/.cargo/config.toml @@ -2,7 +2,7 @@ # replace STM32F429ZITx with your chip as listed in `probe-rs chip list` #runner = "probe-rs run --chip STM32L475VGT6" #runner = "probe-rs run --chip STM32L475VG" -runner = "probe-run --chip STM32L4S5QI" +runner = "probe-rs run --chip STM32L4S5QI" [build] target = "thumbv7em-none-eabi" diff --git a/examples/stm32u0/Cargo.toml b/examples/stm32u0/Cargo.toml index 495be3e75..5868372dd 100644 --- a/examples/stm32u0/Cargo.toml +++ b/examples/stm32u0/Cargo.toml @@ -6,10 +6,11 @@ license = "MIT OR Apache-2.0" [dependencies] # Change stm32u083rc to your chip name, if necessary. -embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti"] } +embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = [ "defmt", "time-driver-any", "stm32u083rc", "memory-x", "unstable-pac", "exti", "chrono"] } embassy-sync = { version = "0.5.0", path = "../../embassy-sync", features = ["defmt"] } embassy-executor = { version = "0.5.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] } embassy-time = { version = "0.3.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", default-features = false, features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" @@ -21,5 +22,8 @@ panic-probe = { version = "0.3", features = ["print-defmt"] } futures = { version = "0.3.17", default-features = false, features = ["async-await"] } heapless = { version = "0.8", default-features = false } +micromath = "2.0.0" +chrono = { version = "0.4.38", default-features = false } + [profile.release] debug = 2 diff --git a/examples/stm32u0/src/bin/adc.rs b/examples/stm32u0/src/bin/adc.rs new file mode 100644 index 000000000..4410448f1 --- /dev/null +++ b/examples/stm32u0/src/bin/adc.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::adc::{Adc, Resolution}; +use embassy_stm32::Config; +use embassy_time::Duration; +use {defmt_rtt as _, panic_probe as _}; + +#[cortex_m_rt::entry] +fn main() -> ! { + info!("Hello World!"); + + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.mux.adcsel = mux::Adcsel::SYS; + } + let p = embassy_stm32::init(config); + + let mut adc = Adc::new(p.ADC1); + adc.set_resolution(Resolution::BITS8); + let mut channel = p.PC0; + + loop { + let v = adc.read(&mut channel); + info!("--> {}", v); + embassy_time::block_for(Duration::from_millis(200)); + } +} diff --git a/examples/stm32u0/src/bin/crc.rs b/examples/stm32u0/src/bin/crc.rs new file mode 100644 index 000000000..d1b545d5b --- /dev/null +++ b/examples/stm32u0/src/bin/crc.rs @@ -0,0 +1,31 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::crc::{Config, Crc, InputReverseConfig, PolySize}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + // Setup for: https://crccalc.com/?crc=Life, it never dieWomen are my favorite guy&method=crc32&datatype=ascii&outtype=0 + let mut crc = Crc::new( + p.CRC, + unwrap!(Config::new( + InputReverseConfig::Byte, + true, + PolySize::Width32, + 0xFFFFFFFF, + 0x04C11DB7 + )), + ); + + let output = crc.feed_bytes(b"Life, it never die\nWomen are my favorite guy") ^ 0xFFFFFFFF; + + defmt::assert_eq!(output, 0x33F0E26B); + + cortex_m::asm::bkpt(); +} diff --git a/examples/stm32u0/src/bin/dac.rs b/examples/stm32u0/src/bin/dac.rs new file mode 100644 index 000000000..fdbf1d374 --- /dev/null +++ b/examples/stm32u0/src/bin/dac.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::dac::{DacCh1, Value}; +use embassy_stm32::dma::NoDma; +use {defmt_rtt as _, panic_probe as _}; + +#[cortex_m_rt::entry] +fn main() -> ! { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut dac = DacCh1::new(p.DAC1, NoDma, p.PA4); + + loop { + for v in 0..=255 { + dac.set(Value::Bit8(to_sine_wave(v))); + } + } +} + +use micromath::F32Ext; + +fn to_sine_wave(v: u8) -> u8 { + if v >= 128 { + // top half + let r = 3.14 * ((v - 128) as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } else { + // bottom half + let r = 3.14 + 3.14 * (v as f32 / 128.0); + (r.sin() * 128.0 + 127.0) as u8 + } +} diff --git a/examples/stm32u0/src/bin/flash.rs b/examples/stm32u0/src/bin/flash.rs new file mode 100644 index 000000000..01b80a76b --- /dev/null +++ b/examples/stm32u0/src/bin/flash.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::flash::Flash; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let addr: u32 = 0x40000 - 2 * 1024; + + let mut f = Flash::new_blocking(p.FLASH).into_blocking_regions().bank1_region; + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(addr, &mut buf)); + info!("Read: {=[u8]:x}", buf); + info!("Erasing..."); + unwrap!(f.blocking_erase(addr, addr + 2 * 1024)); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(addr, &mut buf)); + info!("Read after erase: {=[u8]:x}", buf); + + info!("Writing..."); + unwrap!(f.blocking_write( + addr, + &[ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32 + ] + )); + + info!("Reading..."); + let mut buf = [0u8; 32]; + unwrap!(f.blocking_read(addr, &mut buf)); + info!("Read: {=[u8]:x}", buf); +} diff --git a/examples/stm32u0/src/bin/i2c.rs b/examples/stm32u0/src/bin/i2c.rs new file mode 100644 index 000000000..2861bc091 --- /dev/null +++ b/examples/stm32u0/src/bin/i2c.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::i2c::I2c; +use embassy_stm32::time::Hertz; +use {defmt_rtt as _, panic_probe as _}; + +const ADDRESS: u8 = 0x5F; +const WHOAMI: u8 = 0x0F; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + let mut i2c = I2c::new_blocking(p.I2C2, p.PB10, p.PB11, Hertz(100_000), Default::default()); + + let mut data = [0u8; 1]; + unwrap!(i2c.blocking_write_read(ADDRESS, &[WHOAMI], &mut data)); + info!("Whoami: {}", data[0]); +} diff --git a/examples/stm32u0/src/bin/rng.rs b/examples/stm32u0/src/bin/rng.rs new file mode 100644 index 000000000..89445b042 --- /dev/null +++ b/examples/stm32u0/src/bin/rng.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rcc::mux::Clk48sel; +use embassy_stm32::rng::Rng; +use embassy_stm32::{bind_interrupts, peripherals, rng, Config}; +use {defmt_rtt as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + RNG_CRYP => rng::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, // 16 MHz + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL7, // 16 * 7 = 112 MHz + divp: None, + divq: None, + divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz + }); + config.rcc.sys = Sysclk::PLL1_R; + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: false }); // needed for RNG + config.rcc.mux.clk48sel = Clk48sel::HSI48; // needed for RNG (or use MSI or PLLQ if you want) + } + + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + let mut rng = Rng::new(p.RNG, Irqs); + + let mut buf = [0u8; 16]; + unwrap!(rng.async_fill_bytes(&mut buf).await); + info!("random bytes: {:02x}", buf); +} diff --git a/examples/stm32u0/src/bin/rtc.rs b/examples/stm32u0/src/bin/rtc.rs new file mode 100644 index 000000000..72fa0fde4 --- /dev/null +++ b/examples/stm32u0/src/bin/rtc.rs @@ -0,0 +1,49 @@ +#![no_std] +#![no_main] + +use chrono::{NaiveDate, NaiveDateTime}; +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::rtc::{Rtc, RtcConfig}; +use embassy_stm32::Config; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.sys = Sysclk::PLL1_R; + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, // 16 MHz + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL7, // 16 * 7 = 112 MHz + divp: None, + divq: None, + divr: Some(PllRDiv::DIV2), // 112 / 2 = 56 MHz + }); + config.rcc.ls = LsConfig::default(); + } + + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + let now = NaiveDate::from_ymd_opt(2020, 5, 15) + .unwrap() + .and_hms_opt(10, 30, 15) + .unwrap(); + + let mut rtc = Rtc::new(p.RTC, RtcConfig::default()); + info!("Got RTC! {:?}", now.and_utc().timestamp()); + + rtc.set_datetime(now.into()).expect("datetime not set"); + + // In reality the delay would be much longer + Timer::after_millis(20000).await; + + let then: NaiveDateTime = rtc.now().unwrap().into(); + info!("Got RTC! {:?}", then.and_utc().timestamp()); +} diff --git a/examples/stm32u0/src/bin/spi.rs b/examples/stm32u0/src/bin/spi.rs new file mode 100644 index 000000000..5693a3765 --- /dev/null +++ b/examples/stm32u0/src/bin/spi.rs @@ -0,0 +1,30 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::spi::{Config, Spi}; +use embassy_stm32::time::Hertz; +use {defmt_rtt as _, panic_probe as _}; + +#[cortex_m_rt::entry] +fn main() -> ! { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let mut spi_config = Config::default(); + spi_config.frequency = Hertz(1_000_000); + + let mut spi = Spi::new_blocking(p.SPI3, p.PC10, p.PC12, p.PC11, spi_config); + + let mut cs = Output::new(p.PE0, Level::High, Speed::VeryHigh); + + loop { + let mut buf = [0x0Au8; 4]; + cs.set_low(); + unwrap!(spi.blocking_transfer_in_place(&mut buf)); + cs.set_high(); + info!("xfer {=[u8]:x}", buf); + } +} diff --git a/examples/stm32u0/src/bin/usart.rs b/examples/stm32u0/src/bin/usart.rs new file mode 100644 index 000000000..037a5c833 --- /dev/null +++ b/examples/stm32u0/src/bin/usart.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_stm32::usart::{Config, Uart}; +use {defmt_rtt as _, panic_probe as _}; + +#[cortex_m_rt::entry] +fn main() -> ! { + info!("Hello World!"); + + let p = embassy_stm32::init(Default::default()); + + let config = Config::default(); + let mut usart = Uart::new_blocking(p.USART2, p.PA3, p.PA2, config).unwrap(); + + unwrap!(usart.blocking_write(b"Hello Embassy World!\r\n")); + info!("wrote Hello, starting echo"); + + let mut buf = [0u8; 1]; + loop { + unwrap!(usart.blocking_read(&mut buf)); + unwrap!(usart.blocking_write(&buf)); + } +} diff --git a/examples/stm32u0/src/bin/usb_serial.rs b/examples/stm32u0/src/bin/usb_serial.rs new file mode 100644 index 000000000..9b38fd5dc --- /dev/null +++ b/examples/stm32u0/src/bin/usb_serial.rs @@ -0,0 +1,109 @@ +#![no_std] +#![no_main] + +use defmt::{panic, *}; +use defmt_rtt as _; // global logger +use embassy_executor::Spawner; +use embassy_stm32::usb::{Driver, Instance}; +use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; +use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; +use embassy_usb::driver::EndpointError; +use embassy_usb::Builder; +use futures::future::join; +use panic_probe as _; + +bind_interrupts!(struct Irqs { + USB_DRD_FS => usb::InterruptHandler; +}); + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let mut config = Config::default(); + { + use embassy_stm32::rcc::*; + config.rcc.hsi = true; + config.rcc.pll = Some(Pll { + source: PllSource::HSI, // 16 MHz + prediv: PllPreDiv::DIV1, + mul: PllMul::MUL7, + divp: None, + divq: None, + divr: Some(PllRDiv::DIV2), // 56 MHz + }); + config.rcc.sys = Sysclk::PLL1_R; + config.rcc.hsi48 = Some(Hsi48Config { sync_from_usb: true }); // needed for USB + config.rcc.mux.clk48sel = mux::Clk48sel::HSI48; // USB uses ICLK + } + + let p = embassy_stm32::init(config); + + info!("Hello World!"); + + // Create the driver, from the HAL. + let driver = Driver::new(p.USB, Irqs, p.PA12, p.PA11); + + // Create embassy-usb Config + let config = embassy_usb::Config::new(0xc0de, 0xcafe); + //config.max_packet_size_0 = 64; + + // Create embassy-usb DeviceBuilder using the driver and config. + // It needs some buffers for building the descriptors. + let mut config_descriptor = [0; 256]; + let mut bos_descriptor = [0; 256]; + let mut control_buf = [0; 7]; + + let mut state = State::new(); + + let mut builder = Builder::new( + driver, + config, + &mut config_descriptor, + &mut bos_descriptor, + &mut [], // no msos descriptors + &mut control_buf, + ); + + // Create classes on the builder. + let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); + + // Build the builder. + let mut usb = builder.build(); + + // Run the USB device. + let usb_fut = usb.run(); + + // Do stuff with the class! + let echo_fut = async { + loop { + class.wait_connection().await; + info!("Connected"); + let _ = echo(&mut class).await; + info!("Disconnected"); + } + }; + + // Run everything concurrently. + // If we had made everything `'static` above instead, we could do this using separate tasks instead. + join(usb_fut, echo_fut).await; +} + +struct Disconnected {} + +impl From for Disconnected { + fn from(val: EndpointError) -> Self { + match val { + EndpointError::BufferOverflow => panic!("Buffer overflow"), + EndpointError::Disabled => Disconnected {}, + } + } +} + +async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { + let mut buf = [0; 64]; + loop { + let n = class.read_packet(&mut buf).await?; + let data = &buf[..n]; + info!("data: {:x}", data); + class.write_packet(data).await?; + } +} diff --git a/examples/stm32u0/src/bin/wdt.rs b/examples/stm32u0/src/bin/wdt.rs new file mode 100644 index 000000000..f6276e2e9 --- /dev/null +++ b/examples/stm32u0/src/bin/wdt.rs @@ -0,0 +1,41 @@ +#![no_std] +#![no_main] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_stm32::gpio::{Level, Output, Speed}; +use embassy_stm32::wdg::IndependentWatchdog; +use embassy_time::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_stm32::init(Default::default()); + info!("Hello World!"); + + let mut led = Output::new(p.PA5, Level::High, Speed::Low); + + let mut wdt = IndependentWatchdog::new(p.IWDG, 1_000_000); + wdt.unleash(); + + let mut i = 0; + + loop { + info!("high"); + led.set_high(); + Timer::after_millis(300).await; + + info!("low"); + led.set_low(); + Timer::after_millis(300).await; + + // Pet watchdog for 5 iterations and then stop. + // MCU should restart in 1 second after the last pet. + if i < 5 { + info!("Petting watchdog"); + wdt.pet(); + } + + i += 1; + } +} diff --git a/examples/stm32wb/.cargo/config.toml b/examples/stm32wb/.cargo/config.toml index 51c499ee7..8b6d6d754 100644 --- a/examples/stm32wb/.cargo/config.toml +++ b/examples/stm32wb/.cargo/config.toml @@ -1,6 +1,6 @@ [target.'cfg(all(target_arch = "arm", target_os = "none"))'] # replace STM32WB55CCUx with your chip as listed in `probe-rs chip list` -# runner = "probe-run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" +# runner = "probe-rs run --chip STM32WB55RGVx --speed 1000 --connect-under-reset" runner = "teleprobe local run --chip STM32WB55RG --elf" [build]