aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--embassy-stm32/Cargo.toml6
-rw-r--r--embassy-stm32/build.rs8
-rw-r--r--embassy-stm32/src/lib.rs4
-rw-r--r--embassy-stm32/src/usb_otg_fs.rs159
-rw-r--r--embassy-stm32/src/usb_otg_hs.rs147
-rw-r--r--examples/stm32f4/Cargo.toml5
-rw-r--r--examples/stm32f4/src/bin/usb_uart.rs99
7 files changed, 427 insertions, 1 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 1aa665fb6..8f6504b50 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"], 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,11 @@ memory-x = ["stm32-metapac/memory-x"]
43subghz = [] 44subghz = []
44exti = [] 45exti = []
45 46
47# These features are exclusive
48# synopsys-usb-otg does not support simultaneous FS and HS
49usb-otg-fs = ["synopsys-usb-otg", "synopsys-usb-otg/fs"]
50usb-otg-hs = ["synopsys-usb-otg", "synopsys-usb-otg/hs"]
51
46# Features starting with `_` are for internal use only. They're not intended 52# Features starting with `_` are for internal use only. They're not intended
47# to be enabled by other crates, and are not covered by semver guarantees. 53# to be enabled by other crates, and are not covered by semver guarantees.
48_time-driver = ["embassy/time-tick-32768hz"] 54_time-driver = ["embassy/time-tick-32768hz"]
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 dfc027733..3b9a61d45 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -50,6 +50,10 @@ pub mod sdmmc;
50pub mod spi; 50pub mod spi;
51#[cfg(usart)] 51#[cfg(usart)]
52pub mod usart; 52pub mod usart;
53#[cfg(feature = "usb-otg-fs")]
54pub mod usb_otg_fs;
55#[cfg(feature = "usb-otg-hs")]
56pub mod usb_otg_hs;
53 57
54#[cfg(feature = "subghz")] 58#[cfg(feature = "subghz")]
55pub mod subghz; 59pub mod subghz;
diff --git a/embassy-stm32/src/usb_otg_fs.rs b/embassy-stm32/src/usb_otg_fs.rs
new file mode 100644
index 000000000..3e0cadb11
--- /dev/null
+++ b/embassy-stm32/src/usb_otg_fs.rs
@@ -0,0 +1,159 @@
1use crate::gpio::sealed::{AFType, Pin};
2use crate::{peripherals, rcc::RccPeripheral};
3use core::marker::PhantomData;
4use embassy::util::Unborrow;
5use embassy_hal_common::unborrow;
6pub use embassy_hal_common::usb::*;
7pub use synopsys_usb_otg::UsbBus;
8use synopsys_usb_otg::UsbPeripheral;
9
10pub struct UsbOtgFs<'d, T: Instance> {
11 phantom: PhantomData<&'d mut T>,
12}
13
14impl<'d, T: Instance> UsbOtgFs<'d, T> {
15 pub fn new(
16 _peri: impl Unborrow<Target = T> + 'd,
17 dp: impl Unborrow<Target = impl DpPin<T>> + 'd,
18 dm: impl Unborrow<Target = impl DmPin<T>> + 'd,
19 ) -> Self {
20 unborrow!(dp, dm);
21
22 unsafe {
23 dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
24 dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
25 }
26
27 Self {
28 phantom: PhantomData,
29 }
30 }
31}
32
33impl<'d, T: Instance> Drop for UsbOtgFs<'d, T> {
34 fn drop(&mut self) {
35 T::reset();
36 T::disable();
37 }
38}
39
40unsafe impl<'d, T: Instance> Send for UsbOtgFs<'d, T> {}
41unsafe impl<'d, T: Instance> Sync for UsbOtgFs<'d, T> {}
42
43unsafe impl<'d, T: Instance> UsbPeripheral for UsbOtgFs<'d, T> {
44 const REGISTERS: *const () = T::REGISTERS;
45
46 const HIGH_SPEED: bool = false;
47
48 cfg_if::cfg_if! {
49 if #[cfg(stm32f1)] {
50 const FIFO_DEPTH_WORDS: usize = 128;
51 const ENDPOINT_COUNT: usize = 8;
52 } else if #[cfg(any(
53 stm32f2,
54 stm32f401,
55 stm32f405,
56 stm32f407,
57 stm32f411,
58 stm32f415,
59 stm32f417,
60 stm32f427,
61 stm32f429,
62 stm32f437,
63 stm32f439,
64 ))] {
65 const FIFO_DEPTH_WORDS: usize = 320;
66 const ENDPOINT_COUNT: usize = 4;
67 } else if #[cfg(any(
68 stm32f412,
69 stm32f413,
70 stm32f423,
71 stm32f446,
72 stm32f469,
73 stm32f479,
74 stm32f7,
75 stm32l4,
76 stm32u5,
77 ))] {
78 const FIFO_DEPTH_WORDS: usize = 320;
79 const ENDPOINT_COUNT: usize = 6;
80 } else if #[cfg(stm32g0x1)] {
81 const FIFO_DEPTH_WORDS: usize = 512;
82 const ENDPOINT_COUNT: usize = 8;
83 } else {
84 compile_error!("USB_OTG_FS peripheral is not supported by this chip. Disable \"usb-otg-fs\" feature or select a different chip.");
85 }
86 }
87
88 fn enable() {
89 <T as crate::rcc::sealed::RccPeripheral>::enable();
90 <T as crate::rcc::sealed::RccPeripheral>::reset();
91 }
92
93 fn ahb_frequency_hz(&self) -> u32 {
94 <T as crate::rcc::sealed::RccPeripheral>::frequency().0
95 }
96}
97
98pub(crate) mod sealed {
99 use super::*;
100
101 pub trait Instance {
102 const REGISTERS: *const ();
103 }
104
105 pub trait DpPin<T: Instance>: Pin {
106 fn af_num(&self) -> u8;
107 }
108
109 pub trait DmPin<T: Instance>: Pin {
110 fn af_num(&self) -> u8;
111 }
112}
113
114pub trait Instance: sealed::Instance + RccPeripheral {}
115pub trait DpPin<T: Instance>: sealed::DpPin<T> {}
116pub trait DmPin<T: Instance>: sealed::DmPin<T> {}
117
118crate::pac::peripherals!(
119 (otgfs, $inst:ident) => {
120 impl sealed::Instance for crate::peripherals::$inst {
121 const REGISTERS: *const () = crate::pac::$inst.0 as *const ();
122 }
123
124 impl Instance for peripherals::$inst {}
125 };
126);
127
128crate::pac::interrupts!(
129 ($inst:ident, otgfs, $block:ident, GLOBAL, $irq:ident) => {
130 unsafe impl USBInterrupt for crate::interrupt::$irq {}
131 };
132);
133
134macro_rules! impl_pin {
135 ($inst:ident, $pin:ident, $signal:ident, $af:expr) => {
136 impl $signal<peripherals::$inst> for peripherals::$pin {}
137
138 impl sealed::$signal<peripherals::$inst> for peripherals::$pin {
139 fn af_num(&self) -> u8 {
140 $af
141 }
142 }
143 };
144}
145
146crate::pac::peripheral_pins!(
147 ($inst:ident, otgfs, OTG_FS, $pin:ident, DP, $af:expr) => {
148 impl_pin!($inst, $pin, DpPin, $af);
149 };
150 ($inst:ident, otgfs, OTG_FS, $pin:ident, DM, $af:expr) => {
151 impl_pin!($inst, $pin, DmPin, $af);
152 };
153 ($inst:ident, otgfs, OTG_FS, $pin:ident, DP) => {
154 impl_pin!($inst, $pin, DpPin, 0);
155 };
156 ($inst:ident, otgfs, OTG_FS, $pin:ident, DM) => {
157 impl_pin!($inst, $pin, DmPin, 0);
158 };
159);
diff --git a/embassy-stm32/src/usb_otg_hs.rs b/embassy-stm32/src/usb_otg_hs.rs
new file mode 100644
index 000000000..8de4601e1
--- /dev/null
+++ b/embassy-stm32/src/usb_otg_hs.rs
@@ -0,0 +1,147 @@
1use crate::gpio::sealed::{AFType, Pin};
2use crate::{peripherals, rcc::RccPeripheral};
3use core::marker::PhantomData;
4use embassy::util::Unborrow;
5use embassy_hal_common::unborrow;
6pub use embassy_hal_common::usb::*;
7pub use synopsys_usb_otg::UsbBus;
8use synopsys_usb_otg::UsbPeripheral;
9
10pub struct UsbOtgHs<'d, T: Instance> {
11 phantom: PhantomData<&'d mut T>,
12}
13
14impl<'d, T: Instance> UsbOtgHs<'d, T> {
15 pub fn new(
16 _peri: impl Unborrow<Target = T> + 'd,
17 dp: impl Unborrow<Target = impl DpPin<T>> + 'd,
18 dm: impl Unborrow<Target = impl DmPin<T>> + 'd,
19 ) -> Self {
20 unborrow!(dp, dm);
21
22 unsafe {
23 dp.set_as_af(dp.af_num(), AFType::OutputPushPull);
24 dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
25 }
26
27 Self {
28 phantom: PhantomData,
29 }
30 }
31}
32
33impl<'d, T: Instance> Drop for UsbOtgHs<'d, T> {
34 fn drop(&mut self) {
35 T::reset();
36 T::disable();
37 }
38}
39
40unsafe impl<'d, T: Instance> Send for UsbOtgHs<'d, T> {}
41unsafe impl<'d, T: Instance> Sync for UsbOtgHs<'d, T> {}
42
43unsafe impl<'d, T: Instance> UsbPeripheral for UsbOtgHs<'d, T> {
44 const REGISTERS: *const () = T::REGISTERS;
45
46 const HIGH_SPEED: bool = true;
47
48 cfg_if::cfg_if! {
49 if #[cfg(any(
50 stm32f2,
51 stm32f405,
52 stm32f407,
53 stm32f415,
54 stm32f417,
55 stm32f427,
56 stm32f429,
57 stm32f437,
58 stm32f439,
59 ))] {
60 const FIFO_DEPTH_WORDS: usize = 1024;
61 const ENDPOINT_COUNT: usize = 6;
62 } else if #[cfg(any(
63 stm32f446,
64 stm32f469,
65 stm32f479,
66 stm32f7,
67 stm32h7,
68 ))] {
69 const FIFO_DEPTH_WORDS: usize = 1024;
70 const ENDPOINT_COUNT: usize = 9;
71 } else {
72 compile_error!("USB_OTG_HS peripheral is not supported by this chip. Disable \"usb-otg-hs\" feature or select a different chip.");
73 }
74 }
75
76 fn enable() {
77 <T as crate::rcc::sealed::RccPeripheral>::enable();
78 <T as crate::rcc::sealed::RccPeripheral>::reset();
79 }
80
81 fn ahb_frequency_hz(&self) -> u32 {
82 <T as crate::rcc::sealed::RccPeripheral>::frequency().0
83 }
84}
85
86pub(crate) mod sealed {
87 use super::*;
88
89 pub trait Instance {
90 const REGISTERS: *const ();
91 }
92
93 pub trait DpPin<T: Instance>: Pin {
94 fn af_num(&self) -> u8;
95 }
96
97 pub trait DmPin<T: Instance>: Pin {
98 fn af_num(&self) -> u8;
99 }
100}
101
102pub trait Instance: sealed::Instance + RccPeripheral {}
103pub trait DpPin<T: Instance>: sealed::DpPin<T> {}
104pub trait DmPin<T: Instance>: sealed::DmPin<T> {}
105
106crate::pac::peripherals!(
107 (otghs, $inst:ident) => {
108 impl sealed::Instance for crate::peripherals::$inst {
109 const REGISTERS: *const () = crate::pac::$inst.0 as *const ();
110 }
111
112 impl Instance for peripherals::$inst {}
113 };
114);
115
116crate::pac::interrupts!(
117 ($inst:ident, otghs, $block:ident, GLOBAL, $irq:ident) => {
118 unsafe impl USBInterrupt for crate::interrupt::$irq {}
119 };
120);
121
122macro_rules! impl_pin {
123 ($inst:ident, $pin:ident, $signal:ident, $af:expr) => {
124 impl $signal<peripherals::$inst> for peripherals::$pin {}
125
126 impl sealed::$signal<peripherals::$inst> for peripherals::$pin {
127 fn af_num(&self) -> u8 {
128 $af
129 }
130 }
131 };
132}
133
134crate::pac::peripheral_pins!(
135 ($inst:ident, otghs, OTG_HS, $pin:ident, DP, $af:expr) => {
136 impl_pin!($inst, $pin, DpPin, $af);
137 };
138 ($inst:ident, otghs, OTG_HS, $pin:ident, DM, $af:expr) => {
139 impl_pin!($inst, $pin, DmPin, $af);
140 };
141 ($inst:ident, otghs, OTG_HS, $pin:ident, DP) => {
142 impl_pin!($inst, $pin, DpPin, 0);
143 };
144 ($inst:ident, otghs, OTG_HS, $pin:ident, DM) => {
145 impl_pin!($inst, $pin, DmPin, 0);
146 };
147);
diff --git a/examples/stm32f4/Cargo.toml b/examples/stm32f4/Cargo.toml
index 67b212d5c..8a5908e27 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-fs"] }
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..7167a28b4
--- /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_fs::{State, Usb, UsbBus, UsbOtgFs, 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 = UsbOtgFs::new(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}