stm32_hal2/usb.rs
1//! USB support, including for simulated COM ports. This module is a thin wrapper required to work with
2//! the `stm32_usbd` crate.
3//!
4//! Requires the `usb` feature.
5//!
6//! Used on F303, L4x2, L4x3, L4x5, L5, G0, and G4. F4, L4x6 and H7 use the `usb_otg` module.
7//! For G0 series, USB is only available on G0B0, G0B1, G0C1, which the PAC doesn't yet differentiate,
8//! and this library doesn't yet support.
9
10/*
11 Small caveat, the pac for the l4x5 exposes a USB peripheral instead
12 of a OTG peripheral, even though this chart
13 https://www.st.com/en/microcontrollers-microprocessors/stm32l4-series.html
14 shows that the stm32l475 has OTG.
15
16 Further inspection shows that the generated pac for the l4x5 has a USB peripheral
17 while the l4r5 has an OTG, but this crate doesn't currently seem to support the
18 l4r5 variant.
19
20 Strangely, the register modification commands for the l4x5 have OTG in their names
21*/
22
23use core::borrow::BorrowMut;
24
25use cfg_if::cfg_if;
26pub use stm32_usbd::UsbBus;
27use stm32_usbd::{MemoryAccess, UsbPeripheral};
28
29// use usb_device::{bus::UsbBusAllocator, prelude::*};
30// use usb_device::class_prelude::UsbBus as UsbBus_;
31// use usbd_serial::{self, SerialPort};
32#[cfg(any(feature = "l4", feature = "l5", feature = "g0"))]
33use crate::pac::PWR;
34use crate::{pac, pac::USB, util::rcc_en_reset};
35
36/// Represents a Universal Serial Bus (USB) peripheral. Functionality is implemented through the
37/// implemented `stm32_usbd::UsbPeripheral` trait.
38pub struct Peripheral {
39 /// USB Register Block
40 pub regs: USB,
41}
42
43unsafe impl Sync for Peripheral {}
44// unsafe impl Send for Peripheral {} // todo: Required/do we want this?
45
46unsafe impl UsbPeripheral for Peripheral {
47 const REGISTERS: *const () = USB::ptr() as *const ();
48
49 // Embedded pull-up resistor on USB_DP line
50 #[cfg(feature = "f3")]
51 const DP_PULL_UP_FEATURE: bool = false;
52
53 #[cfg(not(feature = "f3"))]
54 const DP_PULL_UP_FEATURE: bool = true;
55
56 // Pointer to the endpoint memory
57 // todo: This is the L4 setting. Is this right?
58 // L4 Reference manual, Table 2. USB SRAM is on APB1, at this address:
59 #[cfg(any(feature = "l4", feature = "wb"))]
60 const EP_MEMORY: *const () = 0x4000_6c00 as _;
61
62 #[cfg(feature = "l5")]
63 const EP_MEMORY: *const () = 0x5000_d800 as _;
64
65 #[cfg(feature = "g0")]
66 const EP_MEMORY: *const () = 0x4000_5c00 as _;
67
68 #[cfg(feature = "c0")]
69 const EP_MEMORY: *const () = 0x4000_9800 as _; // Table 7, USBRAM entry
70
71 #[cfg(any(feature = "f3", feature = "g4"))]
72 const EP_MEMORY: *const () = 0x4000_6000 as _;
73
74 // Endpoint memory size in bytes
75 // F303 subvariants have diff mem sizes and bits/word scheme: 0B/C use 512 size and 1x16 bits/word.
76 // F303D/E use 1024 bytes, and 2x16 bits/word.
77 #[cfg(feature = "f3")]
78 const EP_MEMORY_SIZE: usize = 512;
79 // todo: Feature-gate various memory sizes
80
81 #[cfg(any(feature = "l4", feature = "l5", feature = "g4", feature = "wb"))]
82 const EP_MEMORY_SIZE: usize = 1_024;
83
84 #[cfg(feature = "g0")]
85 const EP_MEMORY_SIZE: usize = 2_048;
86
87 #[cfg(feature = "c0")]
88 const EP_MEMORY_SIZE: usize = 2_048; // Table 154.
89
90 // Endpoint memory access scheme.
91 // Set to `true` if "2x16 bits/word" access scheme is used, otherwise set to `false`.
92 #[cfg(any(feature = "l4", feature = "l5", feature = "g4", feature = "wb"))]
93 const EP_MEMORY_ACCESS: MemoryAccess = MemoryAccess::Word16x2;
94
95 #[cfg(any(feature = "f3", feature = "g0"))]
96 // F3 uses 1x16 or 2x16 depending on variant. G0 uses a 32-bit word.
97 const EP_MEMORY_ACCESS: MemoryAccess = MemoryAccess::Word16x2;
98
99 #[cfg(any(feature = "c0", feature = "h5"))]
100 const EP_MEMORY_ACCESS: MemoryAccess = MemoryAccess::Word32x1;
101
102 fn enable() {
103 let rcc = unsafe { &*pac::RCC::ptr() };
104
105 cfg_if! {
106 if #[cfg(feature = "l4")] {
107 cfg_if! {
108 if #[cfg(feature = "l4x5")] {
109 rcc_en_reset!(ahb2, otgfs, rcc); // Why does the l4x5 have USB peripheral but OTG names?
110 } else {
111 rcc_en_reset!(apb1, usbfs, rcc);
112 }
113 }
114 } else if #[cfg(feature = "l5")] {
115 rcc.apb1enr2().modify(|_, w| w.usbfsen().bit(true));
116 rcc.apb1rstr2().modify(|_, w| w.usbfsrst().bit(true));
117 rcc.apb1rstr2().modify(|_ , w| w.usbfsrst().clear_bit());
118 } else if #[cfg(feature = "wb")] {
119 rcc.apb1enr1().modify(|_, w| w.usben().bit(true));
120 rcc.apb1rstr1().modify(|_, w| w.usbfsrst().bit(true));
121 rcc.apb1rstr1().modify(|_ , w| w.usbfsrst().clear_bit());
122 } else { // G0, G4
123 rcc_en_reset!(apb1, usb, rcc);
124 }
125 }
126 }
127
128 fn startup_delay() {
129 // There is a chip specific startup delay, around 1µs.
130 // todo: Come back to this. This value current assumes 1µs
131 // todo for an L4 running at full speed.
132 cortex_m::asm::delay(80);
133 }
134}
135
136/// Type of the UsbBus
137pub type UsbBusType = UsbBus<Peripheral>;
138
139#[cfg(any(feature = "l4", feature = "l5", feature = "g0"))]
140/// Enables the Vdd USB power supply. Note that we also need to enable `PWREN` in APB1,
141/// but we handle this using the RTC setup. Use a raw pointer if doing this without the RTC
142/// already set up.
143pub fn enable_usb_pwr() {
144 // Enable VddUSB
145 let pwr = unsafe { &*pac::PWR::ptr() };
146 pwr.cr2().modify(|_, w| w.usv().bit(true));
147}
148
149// todo: We need to sort out the SerialPort traits to makme this work. Non-trivial.
150
151// /// Helper to handle chunked USB writing. Blocks.
152// pub fn write_usb<B: UsbBus_, RS: BorrowMut<[u8]>, WS: BorrowMut<[u8]>>(
153// usb_serial: SerialPort<'static, B, RS, WS>,
154// usb_dev: &mut UsbDevice<'static, UsbBusType>,
155// buf: &[u8],
156// msg_len: usize
157// ) {
158// let mut offset = 0;
159// while offset < msg_len {
160// match usb_serial.write(&buf[offset..msg_len]) {
161// Ok(0) | Err(UsbError::WouldBlock) => {
162// usb_dev.poll(&mut [usb_serial]);
163// }
164// Ok(written) => offset += written,
165// Err(e) => {
166// // defmt::warn!("USB write error: {:?}", e);
167// break;
168// }
169// }
170// }
171
172// while usb_serial.flush().err() == Some(UsbError::WouldBlock) {
173// usb_dev.poll(&mut [usb_serial]);
174// }
175// }