mirror of
https://github.com/embassy-rs/embassy.git
synced 2025-09-26 20:00:27 +00:00
Merge pull request #4472 from leftger/feat/stm32wba-usb-example
Feat/stm32wba usb example
This commit is contained in:
commit
c3f1b54237
1
ci.sh
1
ci.sh
@ -268,6 +268,7 @@ cargo batch \
|
||||
--- build --release --manifest-path examples/stm32u5/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32u5 \
|
||||
--- build --release --manifest-path examples/stm32wb/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wb \
|
||||
--- build --release --manifest-path examples/stm32wba/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba \
|
||||
--- build --release --manifest-path examples/stm32wba6/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/stm32wba6 \
|
||||
--- build --release --manifest-path examples/stm32wl/Cargo.toml --target thumbv7em-none-eabi --artifact-dir out/examples/stm32wl \
|
||||
--- build --release --manifest-path examples/lpc55s69/Cargo.toml --target thumbv8m.main-none-eabihf --artifact-dir out/examples/lpc55s69 \
|
||||
--- build --release --manifest-path examples/mimxrt1011/Cargo.toml --target thumbv7em-none-eabihf --artifact-dir out/examples/mimxrt1011 \
|
||||
|
@ -546,7 +546,11 @@ fn init_hw(config: Config) -> Peripherals {
|
||||
{
|
||||
use crate::pac::pwr::vals;
|
||||
crate::pac::PWR.svmcr().modify(|w| {
|
||||
w.set_io2sv(vals::Io2sv::B_0X1);
|
||||
w.set_io2sv(if config.enable_independent_io_supply {
|
||||
vals::Io2sv::B_0X1
|
||||
} else {
|
||||
vals::Io2sv::B_0X0
|
||||
});
|
||||
});
|
||||
}
|
||||
#[cfg(stm32u5)]
|
||||
|
@ -108,8 +108,11 @@ fn common_init<T: Instance>() {
|
||||
critical_section::with(|_| {
|
||||
crate::pac::PWR.svmcr().modify(|w| {
|
||||
w.set_usv(crate::pac::pwr::vals::Usv::B_0X1);
|
||||
// w.set_uvmen(true);
|
||||
})
|
||||
});
|
||||
crate::pac::PWR.vosr().modify(|w| {
|
||||
w.set_vdd11usbdis(false);
|
||||
w.set_usbpwren(true);
|
||||
});
|
||||
});
|
||||
|
||||
// Wait for USB power to stabilize
|
||||
@ -119,7 +122,6 @@ fn common_init<T: Instance>() {
|
||||
#[cfg(peri_usb_otg_hs)]
|
||||
{
|
||||
crate::pac::PWR.vosr().modify(|w| {
|
||||
w.set_usbpwren(true);
|
||||
w.set_usbboosten(true);
|
||||
});
|
||||
while !crate::pac::PWR.vosr().read().usbboostrdy() {}
|
||||
|
@ -315,30 +315,35 @@ impl<'d, T: Instance> Bus<'d, T> {
|
||||
|
||||
#[cfg(all(stm32u5, peri_usb_otg_hs))]
|
||||
{
|
||||
crate::pac::SYSCFG.otghsphycr().modify(|w| {
|
||||
w.set_en(true);
|
||||
});
|
||||
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.ahb2enr1().modify(|w| {
|
||||
w.set_usb_otg_hsen(true);
|
||||
w.set_usb_otg_hs_phyen(true);
|
||||
});
|
||||
});
|
||||
|
||||
crate::pac::SYSCFG.otghsphycr().modify(|w| {
|
||||
w.set_en(true);
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(all(stm32wba, peri_usb_otg_hs))]
|
||||
{
|
||||
crate::pac::SYSCFG.otghsphycr().modify(|w| {
|
||||
w.set_en(true);
|
||||
});
|
||||
|
||||
critical_section::with(|_| {
|
||||
crate::pac::RCC.ahb2enr().modify(|w| {
|
||||
w.set_usb_otg_hsen(true);
|
||||
w.set_usb_otg_hs_phyen(true);
|
||||
});
|
||||
});
|
||||
|
||||
crate::pac::SYSCFG.otghsphytuner2().modify(|w| {
|
||||
w.set_compdistune(0b010);
|
||||
w.set_sqrxtune(0b000);
|
||||
});
|
||||
|
||||
crate::pac::SYSCFG.otghsphycr().modify(|w| {
|
||||
w.set_en(true);
|
||||
});
|
||||
}
|
||||
|
||||
let r = T::regs();
|
||||
@ -355,7 +360,7 @@ impl<'d, T: Instance> Bus<'d, T> {
|
||||
match core_id {
|
||||
0x0000_1200 | 0x0000_1100 | 0x0000_1000 => self.inner.config_v1(),
|
||||
0x0000_2000 | 0x0000_2100 | 0x0000_2300 | 0x0000_3000 | 0x0000_3100 => self.inner.config_v2v3(),
|
||||
0x0000_5000 => self.inner.config_v5(),
|
||||
0x0000_5000 | 0x0000_6100 => self.inner.config_v5(),
|
||||
_ => unimplemented!("Unknown USB core id {:X}", core_id),
|
||||
}
|
||||
}
|
||||
|
8
examples/stm32wba6/.cargo/config.toml
Normal file
8
examples/stm32wba6/.cargo/config.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
|
||||
runner = "probe-rs run --chip STM32WBA65RI"
|
||||
|
||||
[build]
|
||||
target = "thumbv8m.main-none-eabihf"
|
||||
|
||||
[env]
|
||||
DEFMT_LOG = "trace"
|
26
examples/stm32wba6/Cargo.toml
Normal file
26
examples/stm32wba6/Cargo.toml
Normal file
@ -0,0 +1,26 @@
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "embassy-stm32wba-examples"
|
||||
version = "0.1.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
embassy-stm32 = { version = "0.2.0", path = "../../embassy-stm32", features = [ "defmt", "stm32wba65ri", "time-driver-any", "memory-x", "exti"] }
|
||||
embassy-sync = { version = "0.7.0", path = "../../embassy-sync", features = ["defmt"] }
|
||||
embassy-executor = { version = "0.7.0", path = "../../embassy-executor", features = ["arch-cortex-m", "executor-thread", "defmt"] }
|
||||
embassy-time = { version = "0.4.0", path = "../../embassy-time", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] }
|
||||
embassy-usb = { version = "0.5.0", path = "../../embassy-usb", features = ["defmt"] }
|
||||
embassy-futures = { version = "0.1.0", path = "../../embassy-futures" }
|
||||
|
||||
defmt = "1.0.1"
|
||||
defmt-rtt = "1.0.0"
|
||||
|
||||
cortex-m = { version = "0.7.6", features = ["inline-asm", "critical-section-single-core"] }
|
||||
cortex-m-rt = "0.7.0"
|
||||
embedded-hal = "1.0.0"
|
||||
panic-probe = { version = "1.0.0", features = ["print-defmt"] }
|
||||
heapless = { version = "0.8", default-features = false }
|
||||
static_cell = "2"
|
||||
|
||||
[profile.release]
|
||||
debug = 2
|
10
examples/stm32wba6/build.rs
Normal file
10
examples/stm32wba6/build.rs
Normal file
@ -0,0 +1,10 @@
|
||||
use std::error::Error;
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
println!("cargo:rerun-if-changed=link.x");
|
||||
println!("cargo:rustc-link-arg-bins=--nmagic");
|
||||
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
||||
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
||||
|
||||
Ok(())
|
||||
}
|
49
examples/stm32wba6/src/bin/adc.rs
Normal file
49
examples/stm32wba6/src/bin/adc.rs
Normal file
@ -0,0 +1,49 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_stm32::adc::{adc4, AdcChannel};
|
||||
use {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: embassy_executor::Spawner) {
|
||||
let config = embassy_stm32::Config::default();
|
||||
|
||||
let mut p = embassy_stm32::init(config);
|
||||
|
||||
// **** ADC4 init ****
|
||||
let mut adc4 = adc4::Adc4::new(p.ADC4);
|
||||
let mut adc4_pin1 = p.PA0; // A4
|
||||
let mut adc4_pin2 = p.PA1; // A5
|
||||
adc4.set_resolution(adc4::Resolution::BITS12);
|
||||
adc4.set_averaging(adc4::Averaging::Samples256);
|
||||
adc4.set_sample_time(adc4::SampleTime::CYCLES1_5);
|
||||
let max4 = adc4::resolution_to_max_count(adc4::Resolution::BITS12);
|
||||
|
||||
// **** ADC4 blocking read ****
|
||||
let raw: u16 = adc4.blocking_read(&mut adc4_pin1);
|
||||
let volt: f32 = 3.0 * raw as f32 / max4 as f32;
|
||||
info!("Read adc4 pin 1 {}", volt);
|
||||
|
||||
let raw: u16 = adc4.blocking_read(&mut adc4_pin2);
|
||||
let volt: f32 = 3.3 * raw as f32 / max4 as f32;
|
||||
info!("Read adc4 pin 2 {}", volt);
|
||||
|
||||
// **** ADC4 async read ****
|
||||
let mut degraded41 = adc4_pin1.degrade_adc();
|
||||
let mut degraded42 = adc4_pin2.degrade_adc();
|
||||
let mut measurements = [0u16; 2];
|
||||
|
||||
// The channels must be in ascending order and can't repeat for ADC4
|
||||
adc4.read(
|
||||
p.GPDMA1_CH1.reborrow(),
|
||||
[&mut degraded42, &mut degraded41].into_iter(),
|
||||
&mut measurements,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let volt2: f32 = 3.3 * measurements[0] as f32 / max4 as f32;
|
||||
let volt1: f32 = 3.0 * measurements[1] as f32 / max4 as f32;
|
||||
info!("Async read 4 pin 1 {}", volt1);
|
||||
info!("Async read 4 pin 2 {}", volt2);
|
||||
}
|
26
examples/stm32wba6/src/bin/blinky.rs
Normal file
26
examples/stm32wba6/src/bin/blinky.rs
Normal file
@ -0,0 +1,26 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::gpio::{Level, Output, Speed};
|
||||
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.PB4, Level::High, Speed::Low);
|
||||
|
||||
loop {
|
||||
info!("high");
|
||||
led.set_high();
|
||||
Timer::after_millis(500).await;
|
||||
|
||||
info!("low");
|
||||
led.set_low();
|
||||
Timer::after_millis(500).await;
|
||||
}
|
||||
}
|
25
examples/stm32wba6/src/bin/button_exti.rs
Normal file
25
examples/stm32wba6/src/bin/button_exti.rs
Normal file
@ -0,0 +1,25 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::exti::ExtiInput;
|
||||
use embassy_stm32::gpio::Pull;
|
||||
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 button = ExtiInput::new(p.PC13, p.EXTI13, Pull::Up);
|
||||
|
||||
info!("Press the USER button...");
|
||||
|
||||
loop {
|
||||
button.wait_for_falling_edge().await;
|
||||
info!("Pressed!");
|
||||
button.wait_for_rising_edge().await;
|
||||
info!("Released!");
|
||||
}
|
||||
}
|
65
examples/stm32wba6/src/bin/pwm.rs
Normal file
65
examples/stm32wba6/src/bin/pwm.rs
Normal file
@ -0,0 +1,65 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::*;
|
||||
use defmt_rtt as _; // global logger
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_stm32::gpio::OutputType;
|
||||
use embassy_stm32::rcc::{
|
||||
AHB5Prescaler, AHBPrescaler, APBPrescaler, PllDiv, PllMul, PllPreDiv, PllSource, Sysclk, VoltageScale,
|
||||
};
|
||||
use embassy_stm32::time::khz;
|
||||
use embassy_stm32::timer::simple_pwm::{PwmPin, SimplePwm};
|
||||
use embassy_stm32::Config;
|
||||
use embassy_time::Timer;
|
||||
use panic_probe as _;
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
info!("Hello World!");
|
||||
|
||||
let mut config = Config::default();
|
||||
// Fine-tune PLL1 dividers/multipliers
|
||||
config.rcc.pll1 = Some(embassy_stm32::rcc::Pll {
|
||||
source: PllSource::HSI,
|
||||
prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz
|
||||
mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO
|
||||
divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)
|
||||
// divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz (NOT USED)
|
||||
divq: None,
|
||||
divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USBOTG)
|
||||
frac: Some(0), // Fractional part (enabled)
|
||||
});
|
||||
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV1;
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV1;
|
||||
config.rcc.apb7_pre = APBPrescaler::DIV1;
|
||||
config.rcc.ahb5_pre = AHB5Prescaler::DIV4;
|
||||
|
||||
// voltage scale for max performance
|
||||
config.rcc.voltage_scale = VoltageScale::RANGE1;
|
||||
// route PLL1_P into the USB‐OTG‐HS block
|
||||
config.rcc.sys = Sysclk::PLL1_R;
|
||||
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
let ch1_pin = PwmPin::new(p.PA2, OutputType::PushPull);
|
||||
let mut pwm = SimplePwm::new(p.TIM3, Some(ch1_pin), None, None, None, khz(10), Default::default());
|
||||
let mut ch1 = pwm.ch1();
|
||||
ch1.enable();
|
||||
|
||||
info!("PWM initialized");
|
||||
info!("PWM max duty {}", ch1.max_duty_cycle());
|
||||
|
||||
loop {
|
||||
ch1.set_duty_cycle_fully_off();
|
||||
Timer::after_millis(300).await;
|
||||
ch1.set_duty_cycle_fraction(1, 4);
|
||||
Timer::after_millis(300).await;
|
||||
ch1.set_duty_cycle_fraction(1, 2);
|
||||
Timer::after_millis(300).await;
|
||||
ch1.set_duty_cycle(ch1.max_duty_cycle() - 1);
|
||||
Timer::after_millis(300).await;
|
||||
}
|
||||
}
|
125
examples/stm32wba6/src/bin/usb_hs_serial.rs
Normal file
125
examples/stm32wba6/src/bin/usb_hs_serial.rs
Normal file
@ -0,0 +1,125 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
use defmt::{panic, *};
|
||||
use embassy_executor::Spawner;
|
||||
use embassy_futures::join::join;
|
||||
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 {defmt_rtt as _, panic_probe as _};
|
||||
|
||||
bind_interrupts!(struct Irqs {
|
||||
USB_OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
|
||||
});
|
||||
|
||||
#[embassy_executor::main]
|
||||
async fn main(_spawner: Spawner) {
|
||||
info!("Hello World!");
|
||||
|
||||
let mut config = Config::default();
|
||||
|
||||
{
|
||||
use embassy_stm32::rcc::*;
|
||||
config.rcc.pll1 = Some(Pll {
|
||||
source: PllSource::HSI,
|
||||
prediv: PllPreDiv::DIV1, // PLLM = 1 → HSI / 1 = 16 MHz
|
||||
mul: PllMul::MUL30, // PLLN = 30 → 16 MHz * 30 = 480 MHz VCO
|
||||
divr: Some(PllDiv::DIV5), // PLLR = 5 → 96 MHz (Sysclk)
|
||||
divq: Some(PllDiv::DIV10), // PLLQ = 10 → 48 MHz
|
||||
divp: Some(PllDiv::DIV30), // PLLP = 30 → 16 MHz (USB_OTG_HS)
|
||||
frac: Some(0), // Fractional part (disabled)
|
||||
});
|
||||
|
||||
config.rcc.ahb_pre = AHBPrescaler::DIV1;
|
||||
config.rcc.apb1_pre = APBPrescaler::DIV1;
|
||||
config.rcc.apb2_pre = APBPrescaler::DIV1;
|
||||
config.rcc.apb7_pre = APBPrescaler::DIV1;
|
||||
config.rcc.ahb5_pre = AHB5Prescaler::DIV4;
|
||||
|
||||
config.rcc.voltage_scale = VoltageScale::RANGE1;
|
||||
config.rcc.mux.otghssel = mux::Otghssel::PLL1_P;
|
||||
config.rcc.sys = Sysclk::PLL1_R;
|
||||
}
|
||||
|
||||
let p = embassy_stm32::init(config);
|
||||
|
||||
// Create the driver, from the HAL.
|
||||
let mut ep_out_buffer = [0u8; 256];
|
||||
let mut config = embassy_stm32::usb::Config::default();
|
||||
// Do not enable vbus_detection. This is a safe default that works in all boards.
|
||||
// However, if your USB device is self-powered (can stay powered on if USB is unplugged), you need
|
||||
// to enable vbus_detection to comply with the USB spec. If you enable it, the board
|
||||
// has to support it or USB won't work at all. See docs on `vbus_detection` for details.
|
||||
config.vbus_detection = false;
|
||||
let driver = Driver::new_hs(p.USB_OTG_HS, Irqs, p.PD6, p.PD7, &mut ep_out_buffer, config);
|
||||
|
||||
// Create embassy-usb Config
|
||||
let mut config = embassy_usb::Config::new(0xc0de, 0xcafe);
|
||||
config.manufacturer = Some("Embassy");
|
||||
config.product = Some("USB-serial example");
|
||||
config.serial_number = Some("12345678");
|
||||
|
||||
// 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; 64];
|
||||
|
||||
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<EndpointError> 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?;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user