diff options
Diffstat (limited to 'examples/stm32wba6')
| -rw-r--r-- | examples/stm32wba6/.cargo/config.toml | 8 | ||||
| -rw-r--r-- | examples/stm32wba6/Cargo.toml | 26 | ||||
| -rw-r--r-- | examples/stm32wba6/build.rs | 10 | ||||
| -rw-r--r-- | examples/stm32wba6/src/bin/adc.rs | 49 | ||||
| -rw-r--r-- | examples/stm32wba6/src/bin/blinky.rs | 26 | ||||
| -rw-r--r-- | examples/stm32wba6/src/bin/button_exti.rs | 25 | ||||
| -rw-r--r-- | examples/stm32wba6/src/bin/pwm.rs | 65 | ||||
| -rw-r--r-- | examples/stm32wba6/src/bin/usb_hs_serial.rs | 125 |
8 files changed, 334 insertions, 0 deletions
diff --git a/examples/stm32wba6/.cargo/config.toml b/examples/stm32wba6/.cargo/config.toml new file mode 100644 index 000000000..1896068d8 --- /dev/null +++ b/examples/stm32wba6/.cargo/config.toml | |||
| @@ -0,0 +1,8 @@ | |||
| 1 | [target.'cfg(all(target_arch = "arm", target_os = "none"))'] | ||
| 2 | runner = "probe-rs run --chip STM32WBA65RI" | ||
| 3 | |||
| 4 | [build] | ||
| 5 | target = "thumbv8m.main-none-eabihf" | ||
| 6 | |||
| 7 | [env] | ||
| 8 | DEFMT_LOG = "trace" | ||
diff --git a/examples/stm32wba6/Cargo.toml b/examples/stm32wba6/Cargo.toml new file mode 100644 index 000000000..19c5e1e75 --- /dev/null +++ b/examples/stm32wba6/Cargo.toml | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | [package] | ||
| 2 | edition = "2021" | ||
| 3 | name = "embassy-stm32wba-examples" | ||
| 4 | version = "0.1.0" | ||
| 5 | license = "MIT OR Apache-2.0" | ||
| 6 | |||
| 7 | [dependencies] | ||
| 8 | embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba65ri", "time-driver-any", "memory-x", "exti"] } | ||
| 9 | embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] } | ||
| 10 | embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] } | ||
| 11 | embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } | ||
| 12 | embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] } | ||
| 13 | embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } | ||
| 14 | |||
| 15 | defmt = "1.0.1" | ||
| 16 | defmt-rtt = "1.0.0" | ||
| 17 | |||
| 18 | cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] } | ||
| 19 | cortex-m-rt = "0.7.0" | ||
| 20 | embedded-hal = "1.0.0" | ||
| 21 | panic-probe = { version = "1.0.0", features = ["print-defmt"] } | ||
| 22 | heapless = { version = "0.8", default-features = false } | ||
| 23 | static_cell = "2" | ||
| 24 | |||
| 25 | [profile.release] | ||
| 26 | debug = 2 | ||
diff --git a/examples/stm32wba6/build.rs b/examples/stm32wba6/build.rs new file mode 100644 index 000000000..8fc6faab8 --- /dev/null +++ b/examples/stm32wba6/build.rs | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | use std::error::Error; | ||
| 2 | |||
| 3 | fn main() -> Result<(), Box<dyn Error>> { | ||
| 4 | println!("cargo:rerun-if-changed=link.x"); | ||
| 5 | println!("cargo:rustc-link-arg-bins=--nmagic"); | ||
| 6 | println!("cargo:rustc-link-arg-bins=-Tlink.x"); | ||
| 7 | println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); | ||
| 8 | |||
| 9 | Ok(()) | ||
| 10 | } | ||
diff --git a/examples/stm32wba6/src/bin/adc.rs b/examples/stm32wba6/src/bin/adc.rs new file mode 100644 index 000000000..a9651d57e --- /dev/null +++ b/examples/stm32wba6/src/bin/adc.rs | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_stm32::adc::{adc4, AdcChannel}; | ||
| 6 | use {defmt_rtt as _, panic_probe as _}; | ||
| 7 | |||
| 8 | #[embassy_executor::main] | ||
| 9 | async fn main(_spawner: embassy_executor::Spawner) { | ||
| 10 | let config = embassy_stm32::Config::default(); | ||
| 11 | |||
| 12 | let mut p = embassy_stm32::init(config); | ||
| 13 | |||
| 14 | // **** ADC4 init **** | ||
| 15 | let mut adc4 = adc4::Adc4::new(p.ADC4); | ||
| 16 | let mut adc4_pin1 = p.PA0; // A4 | ||
| 17 | let mut adc4_pin2 = p.PA1; // A5 | ||
| 18 | adc4.set_resolution(adc4::Resolution::BITS12); | ||
| 19 | adc4.set_averaging(adc4::Averaging::Samples256); | ||
| 20 | adc4.set_sample_time(adc4::SampleTime::CYCLES1_5); | ||
| 21 | let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12); | ||
| 22 | |||
| 23 | // **** ADC4 blocking read **** | ||
| 24 | let raw: u16 = adc4.blocking_read(&mut adc4_pin1); | ||
| 25 | let volt: f32 = 3.0 * raw as f32 / max4 as f32; | ||
| 26 | info!("Read adc4 pin 1 {}", volt); | ||
| 27 | |||
| 28 | let raw: u16 = adc4.blocking_read(&mut adc4_pin2); | ||
| 29 | let volt: f32 = 3.3 * raw as f32 / max4 as f32; | ||
| 30 | info!("Read adc4 pin 2 {}", volt); | ||
| 31 | |||
| 32 | // **** ADC4 async read **** | ||
| 33 | let mut degraded41 = adc4_pin1.degrade_adc(); | ||
| 34 | let mut degraded42 = adc4_pin2.degrade_adc(); | ||
| 35 | let mut measurements = [0u16; 2]; | ||
| 36 | |||
| 37 | // The channels must be in ascending order and can't repeat for ADC4 | ||
| 38 | adc4.read( | ||
| 39 | p.GPDMA1_CH1.reborrow(), | ||
| 40 | [&mut degraded42, &mut degraded41].into_iter(), | ||
| 41 | &mut measurements, | ||
| 42 | ) | ||
| 43 | .await | ||
| 44 | .unwrap(); | ||
| 45 | let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32; | ||
| 46 | let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32; | ||
| 47 | info!("Async read 4 pin 1 {}", volt1); | ||
| 48 | info!("Async read 4 pin 2 {}", volt2); | ||
| 49 | } | ||
diff --git a/examples/stm32wba6/src/bin/blinky.rs b/examples/stm32wba6/src/bin/blinky.rs new file mode 100644 index 000000000..0d803b257 --- /dev/null +++ b/examples/stm32wba6/src/bin/blinky.rs | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::gpio::{Level, Output, Speed}; | ||
| 7 | use embassy_time::Timer; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) { | ||
| 12 | let p = embassy_stm32::init(Default::default()); | ||
| 13 | info!("Hello World!"); | ||
| 14 | |||
| 15 | let mut led = Output::new(p.PB4, Level::High, Speed::Low); | ||
| 16 | |||
| 17 | loop { | ||
| 18 | info!("high"); | ||
| 19 | led.set_high(); | ||
| 20 | Timer::after_millis(500).await; | ||
| 21 | |||
| 22 | info!("low"); | ||
| 23 | led.set_low(); | ||
| 24 | Timer::after_millis(500).await; | ||
| 25 | } | ||
| 26 | } | ||
diff --git a/examples/stm32wba6/src/bin/button_exti.rs b/examples/stm32wba6/src/bin/button_exti.rs new file mode 100644 index 000000000..34a08bbc6 --- /dev/null +++ b/examples/stm32wba6/src/bin/button_exti.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_stm32::exti::ExtiInput; | ||
| 7 | use embassy_stm32::gpio::Pull; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) { | ||
| 12 | let p = embassy_stm32::init(Default::default()); | ||
| 13 | info!("Hello World!"); | ||
| 14 | |||
| 15 | let mut button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up); | ||
| 16 | |||
| 17 | info!("Press the USER button..."); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | button.wait_for_falling_edge().await; | ||
| 21 | info!("Pressed!"); | ||
| 22 | button.wait_for_rising_edge().await; | ||
| 23 | info!("Released!"); | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/stm32wba6/src/bin/pwm.rs b/examples/stm32wba6/src/bin/pwm.rs new file mode 100644 index 000000000..2c696834a --- /dev/null +++ b/examples/stm32wba6/src/bin/pwm.rs | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::*; | ||
| 5 | use defmt_rtt as _; // global logger | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_stm32::gpio::OutputType; | ||
| 8 | use embassy_stm32::rcc::{ | ||
| 9 | AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale, | ||
| 10 | }; | ||
| 11 | use embassy_stm32::time::khz; | ||
| 12 | use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm}; | ||
| 13 | use embassy_stm32::Config; | ||
| 14 | use embassy_time::Timer; | ||
| 15 | use panic_probe as _; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | info!("Hello World!"); | ||
| 20 | |||
| 21 | let mut config = Config::default(); | ||
| 22 | // Fine-tune PLL1 dividers/multipliers | ||
| 23 | config.rcc.pll1 = Some(embassy_stm32::rcc::Pll { | ||
| 24 | source: PllSource::HSI, | ||
| 25 | prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz | ||
| 26 | mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO | ||
| 27 | divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk) | ||
| 28 | // divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED) | ||
| 29 | divq: None, | ||
| 30 | divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG) | ||
| 31 | frac: Some(0), // Fractional part (enabled) | ||
| 32 | }); | ||
| 33 | |||
| 34 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 35 | config.rcc.apb1_pre = APBPrescaler::DIV1; | ||
| 36 | config.rcc.apb2_pre = APBPrescaler::DIV1; | ||
| 37 | config.rcc.apb7_pre = APBPrescaler::DIV1; | ||
| 38 | config.rcc.ahb5_pre = AHB5Prescaler::DIV4; | ||
| 39 | |||
| 40 | // voltage scale for max performance | ||
| 41 | config.rcc.voltage_scale = VoltageScale::RANGE1; | ||
| 42 | // route PLL1_P into the USB‐OTG‐HS block | ||
| 43 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 44 | |||
| 45 | let p = embassy_stm32::init(config); | ||
| 46 | |||
| 47 | let ch1_pin = PwmPin::new(p.PA2, OutputType::PushPull); | ||
| 48 | let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default()); | ||
| 49 | let mut ch1 = pwm.ch1(); | ||
| 50 | ch1.enable(); | ||
| 51 | |||
| 52 | info!("PWM initialized"); | ||
| 53 | info!("PWM max duty {}", ch1.max_duty_cycle()); | ||
| 54 | |||
| 55 | loop { | ||
| 56 | ch1.set_duty_cycle_fully_off(); | ||
| 57 | Timer::after_millis(300).await; | ||
| 58 | ch1.set_duty_cycle_fraction(1, 4); | ||
| 59 | Timer::after_millis(300).await; | ||
| 60 | ch1.set_duty_cycle_fraction(1, 2); | ||
| 61 | Timer::after_millis(300).await; | ||
| 62 | ch1.set_duty_cycle(ch1.max_duty_cycle() - 1); | ||
| 63 | Timer::after_millis(300).await; | ||
| 64 | } | ||
| 65 | } | ||
diff --git a/examples/stm32wba6/src/bin/usb_hs_serial.rs b/examples/stm32wba6/src/bin/usb_hs_serial.rs new file mode 100644 index 000000000..20bdeaac3 --- /dev/null +++ b/examples/stm32wba6/src/bin/usb_hs_serial.rs | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use defmt::{panic, *}; | ||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_futures::join::join; | ||
| 7 | use embassy_stm32::usb::{Driver, Instance}; | ||
| 8 | use embassy_stm32::{bind_interrupts, peripherals, usb, Config}; | ||
| 9 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 10 | use embassy_usb::driver::EndpointError; | ||
| 11 | use embassy_usb::Builder; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | bind_interrupts!(struct Irqs { | ||
| 15 | USB_OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>; | ||
| 16 | }); | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | info!("Hello World!"); | ||
| 21 | |||
| 22 | let mut config = Config::default(); | ||
| 23 | |||
| 24 | { | ||
| 25 | use embassy_stm32::rcc::*; | ||
| 26 | config.rcc.pll1 = Some(Pll { | ||
| 27 | source: PllSource::HSI, | ||
| 28 | prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz | ||
| 29 | mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO | ||
| 30 | divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk) | ||
| 31 | divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz | ||
| 32 | divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USB_OTG_HS) | ||
| 33 | frac: Some(0), // Fractional part (disabled) | ||
| 34 | }); | ||
| 35 | |||
| 36 | config.rcc.ahb_pre = AHBPrescaler::DIV1; | ||
| 37 | config.rcc.apb1_pre = APBPrescaler::DIV1; | ||
| 38 | config.rcc.apb2_pre = APBPrescaler::DIV1; | ||
| 39 | config.rcc.apb7_pre = APBPrescaler::DIV1; | ||
| 40 | config.rcc.ahb5_pre = AHB5Prescaler::DIV4; | ||
| 41 | |||
| 42 | config.rcc.voltage_scale = VoltageScale::RANGE1; | ||
| 43 | config.rcc.mux.otghssel = mux::Otghssel::PLL1_P; | ||
| 44 | config.rcc.sys = Sysclk::PLL1_R; | ||
| 45 | } | ||
| 46 | |||
| 47 | let p = embassy_stm32::init(config); | ||
| 48 | |||
| 49 | // Create the driver, from the HAL. | ||
| 50 | let mut ep_out_buffer = [0u8; 256]; | ||
| 51 | let mut config = embassy_stm32::usb::Config::default(); | ||
| 52 | // Do not enable vbus_detection. This is a safe default that works in all boards. | ||
| 53 | // However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need | ||
| 54 | // to enable vbus_detection to comply with the USB spec. If you enable it, the board | ||
| 55 | // has to support it or USB won't work at all. See docs on `vbus_detection` for details. | ||
| 56 | config.vbus_detection = false; | ||
| 57 | let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PD6, p.PD7, &mut ep_out_buffer, config); | ||
| 58 | |||
| 59 | // Create embassy-usb Config | ||
| 60 | let mut config = embassy_usb::Config::new(0xc0de, 0xcafe); | ||
| 61 | config.manufacturer = Some("Embassy"); | ||
| 62 | config.product = Some("USB-serial example"); | ||
| 63 | config.serial_number = Some("12345678"); | ||
| 64 | |||
| 65 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 66 | // It needs some buffers for building the descriptors. | ||
| 67 | let mut config_descriptor = [0; 256]; | ||
| 68 | let mut bos_descriptor = [0; 256]; | ||
| 69 | let mut control_buf = [0; 64]; | ||
| 70 | |||
| 71 | let mut state = State::new(); | ||
| 72 | |||
| 73 | let mut builder = Builder::new( | ||
| 74 | driver, | ||
| 75 | config, | ||
| 76 | &mut config_descriptor, | ||
| 77 | &mut bos_descriptor, | ||
| 78 | &mut [], // no msos descriptors | ||
| 79 | &mut control_buf, | ||
| 80 | ); | ||
| 81 | |||
| 82 | // Create classes on the builder. | ||
| 83 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 84 | |||
| 85 | // Build the builder. | ||
| 86 | let mut usb = builder.build(); | ||
| 87 | |||
| 88 | // Run the USB device. | ||
| 89 | let usb_fut = usb.run(); | ||
| 90 | |||
| 91 | // Do stuff with the class! | ||
| 92 | let echo_fut = async { | ||
| 93 | loop { | ||
| 94 | class.wait_connection().await; | ||
| 95 | info!("Connected"); | ||
| 96 | let _ = echo(&mut class).await; | ||
| 97 | info!("Disconnected"); | ||
| 98 | } | ||
| 99 | }; | ||
| 100 | |||
| 101 | // Run everything concurrently. | ||
| 102 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 103 | join(usb_fut, echo_fut).await; | ||
| 104 | } | ||
| 105 | |||
| 106 | struct Disconnected {} | ||
| 107 | |||
| 108 | impl From<EndpointError> for Disconnected { | ||
| 109 | fn from(val: EndpointError) -> Self { | ||
| 110 | match val { | ||
| 111 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 112 | EndpointError::Disabled => Disconnected {}, | ||
| 113 | } | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { | ||
| 118 | let mut buf = [0; 64]; | ||
| 119 | loop { | ||
| 120 | let n = class.read_packet(&mut buf).await?; | ||
| 121 | let data = &buf[..n]; | ||
| 122 | info!("data: {:x}", data); | ||
| 123 | class.write_packet(data).await?; | ||
| 124 | } | ||
| 125 | } | ||
