aboutsummaryrefslogtreecommitdiff
path: root/examples/stm32wba6/src/bin/usb_hs_serial.rs
diff options
context:
space:
mode:
authorGerzain Mata <[email protected]>2025-07-27 17:05:43 -0700
committerGerzain Mata <[email protected]>2025-07-27 17:08:29 -0700
commit9a1f1cc02c7eb83c3b30de2706cd33eab95a221e (patch)
treed50c96281af80dc9aa4960594c08003c9664dbc8 /examples/stm32wba6/src/bin/usb_hs_serial.rs
parent81bef219e315aca005e50143757c681d5bc122ee (diff)
Separated USB_OTG_HS to STM32WBA6
Diffstat (limited to 'examples/stm32wba6/src/bin/usb_hs_serial.rs')
-rw-r--r--examples/stm32wba6/src/bin/usb_hs_serial.rs125
1 files changed, 125 insertions, 0 deletions
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
4use defmt::{panic, *};
5use embassy_executor::Spawner;
6use embassy_futures::join::join;
7use embassy_stm32::usb::{Driver, Instance};
8use embassy_stm32::{bind_interrupts, peripherals, usb, Config};
9use embassy_usb::class::cdc_acm::{CdcAcmClass, State};
10use embassy_usb::driver::EndpointError;
11use embassy_usb::Builder;
12use {defmt_rtt as _, panic_probe as _};
13
14bind_interrupts!(struct Irqs {
15 USB_OTG_HS => usb::InterruptHandler<peripherals::USB_OTG_HS>;
16});
17
18#[embassy_executor::main]
19async 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
106struct Disconnected {}
107
108impl 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
117async 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}