aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-02-09 00:29:00 +0000
committerGitHub <[email protected]>2022-02-09 00:29:00 +0000
commitd91bd0b9a69b8411f2a1d58bfad5d4dce51e7110 (patch)
treed47dea553adf289457ccc292df7317e8907dc7d4
parent2cf79f6569a7d65d70599a62642c7bc353abd692 (diff)
parent10981ee8093492a1b85d69db90269a9f4d8e3cee (diff)
Merge #602
602: Add stm32 USB OTG peripherals r=Dirbaio a=chemicstry Fixes #557. This is similar to #580, but for synopsys IP. I could add examples to other chips, but I have no way of testing them. The F4 example is tested and working. Co-authored-by: chemicstry <[email protected]>
-rw-r--r--embassy-stm32/Cargo.toml2
-rw-r--r--embassy-stm32/build.rs8
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/usb_otg.rs332
-rw-r--r--examples/stm32f4/Cargo.toml5
-rw-r--r--examples/stm32f4/src/bin/usb_uart.rs99
-rw-r--r--examples/stm32f4/src/bin/usb_uart_ulpi.rs114
7 files changed, 561 insertions, 1 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index edee4e3a5..ebd62cbf7 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -23,6 +23,7 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
23rand_core = "0.6.3" 23rand_core = "0.6.3"
24sdio-host = "0.5.0" 24sdio-host = "0.5.0"
25embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true } 25embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true }
26synopsys-usb-otg = { version = "0.3", features = ["cortex-m", "hs"], optional = true }
26critical-section = "0.2.5" 27critical-section = "0.2.5"
27bare-metal = "1.0.0" 28bare-metal = "1.0.0"
28atomic-polyfill = "0.1.5" 29atomic-polyfill = "0.1.5"
@@ -43,6 +44,7 @@ net = ["embassy-net", "vcell"]
43memory-x = ["stm32-metapac/memory-x"] 44memory-x = ["stm32-metapac/memory-x"]
44subghz = [] 45subghz = []
45exti = [] 46exti = []
47usb-otg = ["synopsys-usb-otg"]
46 48
47# Features starting with `_` are for internal use only. They're not intended 49# Features starting with `_` are for internal use only. They're not intended
48# to be enabled by other crates, and are not covered by semver guarantees. 50# to be enabled by other crates, and are not covered by semver guarantees.
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index ff91f93de..aee596445 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -185,6 +185,14 @@ fn main() {
185 println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]); 185 println!("cargo:rustc-cfg={}x{}", &chip_name[..9], &chip_name[10..11]);
186 } 186 }
187 187
188 // =======
189 // Features for targeting groups of chips
190
191 println!("cargo:rustc-cfg={}", &chip_name[..7]); // stm32f4
192 println!("cargo:rustc-cfg={}", &chip_name[..9]); // stm32f429
193 println!("cargo:rustc-cfg={}x", &chip_name[..8]); // stm32f42x
194 println!("cargo:rustc-cfg={}x{}", &chip_name[..7], &chip_name[8..9]); // stm32f4x9
195
188 // ======== 196 // ========
189 // Handle time-driver-XXXX features. 197 // Handle time-driver-XXXX features.
190 198
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 75a4d22d8..4bc044047 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -52,6 +52,8 @@ pub mod sdmmc;
52pub mod spi; 52pub mod spi;
53#[cfg(usart)] 53#[cfg(usart)]
54pub mod usart; 54pub mod usart;
55#[cfg(feature = "usb-otg")]
56pub mod usb_otg;
55 57
56#[cfg(feature = "subghz")] 58#[cfg(feature = "subghz")]
57pub mod subghz; 59pub mod subghz;
diff --git a/embassy-stm32/src/usb_otg.rs b/embassy-stm32/src/usb_otg.rs
new file mode 100644
index 000000000..b3494ee7a
--- /dev/null
+++ b/embassy-stm32/src/usb_otg.rs
@@ -0,0 +1,332 @@
1use crate::{peripherals, rcc::RccPeripheral};
2use core::marker::PhantomData;
3use embassy::util::Unborrow;
4use embassy_hal_common::unborrow;
5pub use embassy_hal_common::usb::*;
6pub use synopsys_usb_otg::UsbBus;
7use synopsys_usb_otg::{PhyType, UsbPeripheral};
8
9macro_rules! config_pins {
10 ($($pin:ident),*) => {
11 $(
12 $pin.configure();
13 )*
14 };
15}
16
17pub struct UsbOtg<'d, T: Instance> {
18 phantom: PhantomData<&'d mut T>,
19 phy_type: PhyType,
20}
21
22impl<'d, T: Instance> UsbOtg<'d, T> {
23 /// Initializes USB OTG peripheral with internal Full-Speed PHY
24 pub fn new_fs(
25 _peri: impl Unborrow<Target = T> + 'd,
26 dp: impl Unborrow<Target = impl DpPin<T>> + 'd,
27 dm: impl Unborrow<Target = impl DmPin<T>> + 'd,
28 ) -> Self {
29 unborrow!(dp, dm);
30 config_pins!(dp, dm);
31
32 Self {
33 phantom: PhantomData,
34 phy_type: PhyType::InternalFullSpeed,
35 }
36 }
37
38 /// Initializes USB OTG peripheral with external High-Speed PHY
39 pub fn new_hs_ulpi(
40 _peri: impl Unborrow<Target = T> + 'd,
41 ulpi_clk: impl Unborrow<Target = impl UlpiClkPin<T>> + 'd,
42 ulpi_dir: impl Unborrow<Target = impl UlpiDirPin<T>> + 'd,
43 ulpi_nxt: impl Unborrow<Target = impl UlpiNxtPin<T>> + 'd,
44 ulpi_stp: impl Unborrow<Target = impl UlpiStpPin<T>> + 'd,
45 ulpi_d0: impl Unborrow<Target = impl UlpiD0Pin<T>> + 'd,
46 ulpi_d1: impl Unborrow<Target = impl UlpiD1Pin<T>> + 'd,
47 ulpi_d2: impl Unborrow<Target = impl UlpiD2Pin<T>> + 'd,
48 ulpi_d3: impl Unborrow<Target = impl UlpiD3Pin<T>> + 'd,
49 ulpi_d4: impl Unborrow<Target = impl UlpiD4Pin<T>> + 'd,
50 ulpi_d5: impl Unborrow<Target = impl UlpiD5Pin<T>> + 'd,
51 ulpi_d6: impl Unborrow<Target = impl UlpiD6Pin<T>> + 'd,
52 ulpi_d7: impl Unborrow<Target = impl UlpiD7Pin<T>> + 'd,
53 ) -> Self {
54 unborrow!(ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp);
55 unborrow!(ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6, ulpi_d7);
56 config_pins!(ulpi_clk, ulpi_dir, ulpi_nxt, ulpi_stp);
57 config_pins!(ulpi_d0, ulpi_d1, ulpi_d2, ulpi_d3, ulpi_d4, ulpi_d5, ulpi_d6, ulpi_d7);
58
59 Self {
60 phantom: PhantomData,
61 phy_type: PhyType::ExternalHighSpeed,
62 }
63 }
64}
65
66impl<'d, T: Instance> Drop for UsbOtg<'d, T> {
67 fn drop(&mut self) {
68 T::reset();
69 T::disable();
70 }
71}
72
73unsafe impl<'d, T: Instance> Send for UsbOtg<'d, T> {}
74unsafe impl<'d, T: Instance> Sync for UsbOtg<'d, T> {}
75
76unsafe impl<'d, T: Instance> UsbPeripheral for UsbOtg<'d, T> {
77 const REGISTERS: *const () = T::REGISTERS;
78 const HIGH_SPEED: bool = T::HIGH_SPEED;
79 const FIFO_DEPTH_WORDS: usize = T::FIFO_DEPTH_WORDS;
80 const ENDPOINT_COUNT: usize = T::ENDPOINT_COUNT;
81
82 fn enable() {
83 <T as crate::rcc::sealed::RccPeripheral>::enable();
84 <T as crate::rcc::sealed::RccPeripheral>::reset();
85 }
86
87 fn phy_type(&self) -> PhyType {
88 self.phy_type
89 }
90
91 fn ahb_frequency_hz(&self) -> u32 {
92 <T as crate::rcc::sealed::RccPeripheral>::frequency().0
93 }
94}
95
96pub(crate) mod sealed {
97 pub trait Instance {
98 const REGISTERS: *const ();
99 const HIGH_SPEED: bool;
100 const FIFO_DEPTH_WORDS: usize;
101 const ENDPOINT_COUNT: usize;
102 }
103
104 macro_rules! declare_pins {
105 ($name:ident) => {
106 pub trait $name<T: Instance> {
107 fn configure(&mut self);
108 }
109 };
110
111 ($($name:ident),*) => {
112 $(
113 declare_pins!($name);
114 )*
115 };
116 }
117
118 // Internal PHY pins
119 declare_pins!(DpPin, DmPin);
120
121 // External PHY pins
122 declare_pins!(UlpiClkPin, UlpiDirPin, UlpiNxtPin, UlpiStpPin);
123 declare_pins!(UlpiD0Pin, UlpiD1Pin, UlpiD2Pin, UlpiD3Pin);
124 declare_pins!(UlpiD4Pin, UlpiD5Pin, UlpiD6Pin, UlpiD7Pin);
125}
126
127pub trait Instance: sealed::Instance + RccPeripheral {}
128
129macro_rules! declare_pins {
130 ($name:ident) => {
131 pub trait $name<T: Instance>: sealed::$name<T> {}
132 };
133
134 ($($name:ident),*) => {
135 $(
136 declare_pins!($name);
137 )*
138 };
139}
140
141declare_pins!(DpPin, DmPin);
142declare_pins!(UlpiClkPin, UlpiDirPin, UlpiNxtPin, UlpiStpPin);
143declare_pins!(UlpiD0Pin, UlpiD1Pin, UlpiD2Pin, UlpiD3Pin);
144declare_pins!(UlpiD4Pin, UlpiD5Pin, UlpiD6Pin, UlpiD7Pin);
145
146crate::pac::peripherals!(
147 (otgfs, $inst:ident) => {
148 impl sealed::Instance for peripherals::$inst {
149 const REGISTERS: *const () = crate::pac::$inst.0 as *const ();
150 const HIGH_SPEED: bool = false;
151
152 cfg_if::cfg_if! {
153 if #[cfg(stm32f1)] {
154 const FIFO_DEPTH_WORDS: usize = 128;
155 const ENDPOINT_COUNT: usize = 8;
156 } else if #[cfg(any(
157 stm32f2,
158 stm32f401,
159 stm32f405,
160 stm32f407,
161 stm32f411,
162 stm32f415,
163 stm32f417,
164 stm32f427,
165 stm32f429,
166 stm32f437,
167 stm32f439,
168 ))] {
169 const FIFO_DEPTH_WORDS: usize = 320;
170 const ENDPOINT_COUNT: usize = 4;
171 } else if #[cfg(any(
172 stm32f412,
173 stm32f413,
174 stm32f423,
175 stm32f446,
176 stm32f469,
177 stm32f479,
178 stm32f7,
179 stm32l4,
180 stm32u5,
181 ))] {
182 const FIFO_DEPTH_WORDS: usize = 320;
183 const ENDPOINT_COUNT: usize = 6;
184 } else if #[cfg(stm32g0x1)] {
185 const FIFO_DEPTH_WORDS: usize = 512;
186 const ENDPOINT_COUNT: usize = 8;
187 } else {
188 compile_error!("USB_OTG_FS peripheral is not supported by this chip. Disable \"usb-otg-fs\" feature or select a different chip.");
189 }
190 }
191 }
192
193 impl Instance for peripherals::$inst {}
194 };
195
196 (otghs, $inst:ident) => {
197 impl sealed::Instance for peripherals::$inst {
198 const REGISTERS: *const () = crate::pac::$inst.0 as *const ();
199 const HIGH_SPEED: bool = true;
200
201 cfg_if::cfg_if! {
202 if #[cfg(any(
203 stm32f2,
204 stm32f405,
205 stm32f407,
206 stm32f415,
207 stm32f417,
208 stm32f427,
209 stm32f429,
210 stm32f437,
211 stm32f439,
212 ))] {
213 const FIFO_DEPTH_WORDS: usize = 1024;
214 const ENDPOINT_COUNT: usize = 6;
215 } else if #[cfg(any(
216 stm32f446,
217 stm32f469,
218 stm32f479,
219 stm32f7,
220 stm32h7,
221 ))] {
222 const FIFO_DEPTH_WORDS: usize = 1024;
223 const ENDPOINT_COUNT: usize = 9;
224 } else {
225 compile_error!("USB_OTG_HS peripheral is not supported by this chip. Disable \"usb-otg-hs\" feature or select a different chip.");
226 }
227 }
228 }
229
230 impl Instance for peripherals::$inst {}
231 };
232);
233
234crate::pac::interrupts!(
235 ($inst:ident, otgfs, $block:ident, GLOBAL, $irq:ident) => {
236 unsafe impl USBInterrupt for crate::interrupt::$irq {}
237 };
238 ($inst:ident, otghs, $block:ident, GLOBAL, $irq:ident) => {
239 unsafe impl USBInterrupt for crate::interrupt::$irq {}
240 };
241);
242
243macro_rules! impl_pin {
244 ($inst:ident, $pin:ident, $signal:ident, $af:expr) => {
245 impl $signal<peripherals::$inst> for peripherals::$pin {}
246
247 impl sealed::$signal<peripherals::$inst> for peripherals::$pin {
248 fn configure(&mut self) {
249 use crate::gpio::sealed::{AFType::OutputPushPull, Pin as SealedPin};
250
251 critical_section::with(|_| unsafe {
252 self.set_as_af($af, OutputPushPull);
253 });
254 }
255 }
256 };
257}
258
259// ULPI pins have to be set to VeryHigh speed
260macro_rules! impl_ulpi_pin {
261 ($inst:ident, $pin:ident, $signal:ident, $af:expr) => {
262 impl $signal<peripherals::$inst> for peripherals::$pin {}
263
264 impl sealed::$signal<peripherals::$inst> for peripherals::$pin {
265 fn configure(&mut self) {
266 use crate::gpio::sealed::{AFType::OutputPushPull, Pin as SealedPin};
267 use crate::gpio::Speed;
268
269 critical_section::with(|_| unsafe {
270 self.set_as_af($af, OutputPushPull);
271 self.set_speed(Speed::VeryHigh);
272 });
273 }
274 }
275 };
276}
277
278crate::pac::peripheral_pins!(
279 // FS internal phy pins
280 ($inst:ident, otgfs, OTG_FS, $pin:ident, DP, $af:expr) => {
281 impl_pin!($inst, $pin, DpPin, $af);
282 };
283 ($inst:ident, otgfs, OTG_FS, $pin:ident, DM, $af:expr) => {
284 impl_pin!($inst, $pin, DmPin, $af);
285 };
286
287 // HS internal phy pins
288 ($inst:ident, otghs, OTG_HS, $pin:ident, DP, $af:expr) => {
289 impl_pin!($inst, $pin, DpPin, $af);
290 };
291 ($inst:ident, otghs, OTG_HS, $pin:ident, DM, $af:expr) => {
292 impl_pin!($inst, $pin, DmPin, $af);
293 };
294
295 // HS external phy pins
296 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_CK, $af:expr) => {
297 impl_ulpi_pin!($inst, $pin, UlpiClkPin, $af);
298 };
299 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_DIR, $af:expr) => {
300 impl_ulpi_pin!($inst, $pin, UlpiDirPin, $af);
301 };
302 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_NXT, $af:expr) => {
303 impl_ulpi_pin!($inst, $pin, UlpiNxtPin, $af);
304 };
305 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_STP, $af:expr) => {
306 impl_ulpi_pin!($inst, $pin, UlpiStpPin, $af);
307 };
308 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_D0, $af:expr) => {
309 impl_ulpi_pin!($inst, $pin, UlpiD0Pin, $af);
310 };
311 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_D1, $af:expr) => {
312 impl_ulpi_pin!($inst, $pin, UlpiD1Pin, $af);
313 };
314 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_D2, $af:expr) => {
315 impl_ulpi_pin!($inst, $pin, UlpiD2Pin, $af);
316 };
317 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_D3, $af:expr) => {
318 impl_ulpi_pin!($inst, $pin, UlpiD3Pin, $af);
319 };
320 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_D4, $af:expr) => {
321 impl_ulpi_pin!($inst, $pin, UlpiD4Pin, $af);
322 };
323 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_D5, $af:expr) => {
324 impl_ulpi_pin!($inst, $pin, UlpiD5Pin, $af);
325 };
326 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_D6, $af:expr) => {
327 impl_ulpi_pin!($inst, $pin, UlpiD6Pin, $af);
328 };
329 ($inst:ident, otghs, OTG_HS, $pin:ident, ULPI_D7, $af:expr) => {
330 impl_ulpi_pin!($inst, $pin, UlpiD7Pin, $af);
331 };
332);
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index 67b212d5c..798144c2b 100644
--- a/examples/stm32f4/Cargo.toml
+++ b/examples/stm32f4/Cargo.toml
@@ -8,7 +8,7 @@ resolver = "2"
8 8
9[dependencies] 9[dependencies]
10embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "unstable-traits"] } 10embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "unstable-traits"] }
11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] } 11embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "usb-otg"] }
12 12
13defmt = "0.3" 13defmt = "0.3"
14defmt-rtt = "0.3" 14defmt-rtt = "0.3"
@@ -20,3 +20,6 @@ panic-probe = { version = "0.3", features = ["print-defmt"] }
20futures = { version = "0.3.17", default-features = false, features = ["async-await"] } 20futures = { version = "0.3.17", default-features = false, features = ["async-await"] }
21heapless = { version = "0.7.5", default-features = false } 21heapless = { version = "0.7.5", default-features = false }
22nb = "1.0.0" 22nb = "1.0.0"
23
24usb-device = "0.2"
25usbd-serial = "0.1.1"
diff --git a/examples/stm32f4/src/bin/usb_uart.rs b/examples/stm32f4/src/bin/usb_uart.rs
new file mode 100644
index 000000000..63059d51f
--- /dev/null
+++ b/examples/stm32f4/src/bin/usb_uart.rs
@@ -0,0 +1,99 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7
8use defmt::{info, unwrap};
9use defmt_rtt as _; // global logger
10use embassy::interrupt::InterruptExt;
11use futures::pin_mut;
12use panic_probe as _; // print out panic messages
13
14use embassy::executor::Spawner;
15use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
16use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial};
17use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals};
18use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
19
20static mut EP_MEMORY: [u32; 2048] = [0; 2048];
21
22// USB requires at least 48 MHz clock
23fn config() -> Config {
24 let mut config = Config::default();
25 config.rcc.sys_ck = Some(Hertz(48_000_000));
26 config
27}
28
29#[embassy::main(config = "config()")]
30async fn main(_spawner: Spawner, p: Peripherals) {
31 let mut rx_buffer = [0u8; 64];
32 // we send back input + cr + lf
33 let mut tx_buffer = [0u8; 66];
34
35 let peri = UsbOtg::new_fs(p.USB_OTG_FS, p.PA12, p.PA11);
36 let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY });
37
38 let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
39
40 let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
41 .manufacturer("Fake company")
42 .product("Serial port")
43 .serial_number("TEST")
44 .device_class(0x02)
45 .build();
46
47 let irq = interrupt::take!(OTG_FS);
48 irq.set_priority(interrupt::Priority::P3);
49
50 let mut state = State::new();
51 let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
52 pin_mut!(usb);
53
54 let (mut reader, mut writer) = usb.as_ref().take_serial_0();
55
56 info!("usb initialized!");
57
58 unwrap!(
59 writer
60 .write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
61 .await
62 );
63
64 let mut buf = [0u8; 64];
65 loop {
66 let mut n = 0;
67
68 async {
69 loop {
70 let char = unwrap!(reader.read_byte().await);
71
72 if char == b'\r' || char == b'\n' {
73 break;
74 }
75
76 buf[n] = char;
77 n += 1;
78
79 // stop if we're out of room
80 if n == buf.len() {
81 break;
82 }
83 }
84 }
85 .await;
86
87 if n > 0 {
88 for char in buf[..n].iter_mut() {
89 // upper case
90 if 0x61 <= *char && *char <= 0x7a {
91 *char &= !0x20;
92 }
93 }
94 unwrap!(writer.write_all(&buf[..n]).await);
95 unwrap!(writer.write_all(b"\r\n").await);
96 unwrap!(writer.flush().await);
97 }
98 }
99}
diff --git a/examples/stm32f4/src/bin/usb_uart_ulpi.rs b/examples/stm32f4/src/bin/usb_uart_ulpi.rs
new file mode 100644
index 000000000..a55502728
--- /dev/null
+++ b/examples/stm32f4/src/bin/usb_uart_ulpi.rs
@@ -0,0 +1,114 @@
1#![no_std]
2#![no_main]
3#![feature(type_alias_impl_trait)]
4
5#[path = "../example_common.rs"]
6mod example_common;
7
8use defmt::{info, unwrap};
9use defmt_rtt as _; // global logger
10use embassy::interrupt::InterruptExt;
11use futures::pin_mut;
12use panic_probe as _; // print out panic messages
13
14use embassy::executor::Spawner;
15use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
16use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial};
17use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals};
18use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
19
20static mut EP_MEMORY: [u32; 2048] = [0; 2048];
21
22// USB requires at least 48 MHz clock
23fn config() -> Config {
24 let mut config = Config::default();
25 config.rcc.sys_ck = Some(Hertz(48_000_000));
26 config
27}
28
29#[embassy::main(config = "config()")]
30async fn main(_spawner: Spawner, p: Peripherals) {
31 let mut rx_buffer = [0u8; 64];
32 // we send back input + cr + lf
33 let mut tx_buffer = [0u8; 66];
34
35 // USB with external high-speed PHY
36 let peri = UsbOtg::new_hs_ulpi(
37 p.USB_OTG_HS,
38 p.PA5,
39 p.PC2,
40 p.PC3,
41 p.PC0,
42 p.PA3,
43 p.PB0,
44 p.PB1,
45 p.PB10,
46 p.PB11,
47 p.PB12,
48 p.PB13,
49 p.PB5,
50 );
51 let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY });
52
53 let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
54
55 let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
56 .manufacturer("Fake company")
57 .product("Serial port")
58 .serial_number("TEST")
59 .device_class(0x02)
60 .build();
61
62 let irq = interrupt::take!(OTG_FS);
63 irq.set_priority(interrupt::Priority::P3);
64
65 let mut state = State::new();
66 let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
67 pin_mut!(usb);
68
69 let (mut reader, mut writer) = usb.as_ref().take_serial_0();
70
71 info!("usb initialized!");
72
73 unwrap!(
74 writer
75 .write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
76 .await
77 );
78
79 let mut buf = [0u8; 64];
80 loop {
81 let mut n = 0;
82
83 async {
84 loop {
85 let char = unwrap!(reader.read_byte().await);
86
87 if char == b'\r' || char == b'\n' {
88 break;
89 }
90
91 buf[n] = char;
92 n += 1;
93
94 // stop if we're out of room
95 if n == buf.len() {
96 break;
97 }
98 }
99 }
100 .await;
101
102 if n > 0 {
103 for char in buf[..n].iter_mut() {
104 // upper case
105 if 0x61 <= *char && *char <= 0x7a {
106 *char &= !0x20;
107 }
108 }
109 unwrap!(writer.write_all(&buf[..n]).await);
110 unwrap!(writer.write_all(b"\r\n").await);
111 unwrap!(writer.flush().await);
112 }
113 }
114}