py32_hal/
usb.rs

1/// Universal Serial Bus (USB)
2///
3/// The USB peripheral IP in PY32 is a mini Mentor USB (musb),
4/// featuring a fixed FIFO size and with some register functionalities masked.
5///
6/// See more: https://github.com/decaday/musb
7use core::marker::PhantomData;
8
9#[cfg(feature = "embassy-usb-driver-impl")]
10use embassy_usb_driver as driver;
11#[cfg(feature = "embassy-usb-driver-impl")]
12use embassy_usb_driver::{EndpointType, EndpointAddress};
13#[cfg(feature = "usb-device-impl")]
14pub use musb::UsbdBus;
15#[cfg(feature = "embassy-usb-driver-impl")]
16use musb::{Bus, ControlPipe, Endpoint, In, MusbDriver, Out};
17
18use musb::UsbInstance;
19
20use crate::interrupt::typelevel::Interrupt;
21use crate::rcc::{self, RccPeripheral};
22use crate::{interrupt, Peripheral};
23
24/// Interrupt handler.
25pub struct InterruptHandler<T: Instance> {
26    _phantom: PhantomData<T>,
27}
28
29impl<T: Instance> interrupt::typelevel::Handler<T::Interrupt> for InterruptHandler<T> {
30    unsafe fn on_interrupt() {
31        musb::on_interrupt::<UsbInstance>();
32    }
33}
34
35fn init<T: Instance>() {
36    let freq = T::frequency();
37    if freq.0 != 48_000_000 {
38        panic!("USB clock (PLL) must be 48MHz");
39    }
40
41    T::Interrupt::unpend();
42    unsafe { T::Interrupt::enable() };
43    rcc::enable_and_reset::<T>();
44
45    #[cfg(feature = "time")]
46    embassy_time::block_for(embassy_time::Duration::from_millis(100));
47    #[cfg(not(feature = "time"))]
48    cortex_m::asm::delay(unsafe { crate::rcc::get_freqs() }.sys.to_hertz().unwrap().0 / 10);
49}
50
51#[cfg(feature = "embassy-usb-driver-impl")]
52/// USB driver.
53pub struct Driver<'d, T: Instance> {
54    phantom: PhantomData<&'d mut T>,
55    inner: MusbDriver<'d, UsbInstance>,
56}
57
58#[cfg(feature = "embassy-usb-driver-impl")]
59impl<'d, T: Instance> Driver<'d, T> {
60    /// Create a new USB driver.
61    pub fn new(
62        _usb: impl Peripheral<P = T> + 'd,
63        _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
64        _dp: impl Peripheral<P = impl DpPin<T>> + 'd,
65        _dm: impl Peripheral<P = impl DmPin<T>> + 'd,
66    ) -> Self {
67        init::<T>();
68
69        Self {
70            inner: MusbDriver::new(),
71            phantom: PhantomData,
72        }
73    }
74}
75
76#[cfg(feature = "embassy-usb-driver-impl")]
77impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
78    type EndpointOut = Endpoint<'d, UsbInstance, Out>;
79    type EndpointIn = Endpoint<'d, UsbInstance, In>;
80    type ControlPipe = ControlPipe<'d, UsbInstance>;
81    type Bus = Bus<'d, UsbInstance>;
82
83    fn alloc_endpoint_in(
84        &mut self,
85        ep_type: EndpointType,
86        ep_addr: Option<EndpointAddress>,
87        max_packet_size: u16,
88        interval_ms: u8,
89    ) -> Result<Self::EndpointIn, driver::EndpointAllocError> {
90        self.inner
91            .alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)
92    }
93
94    fn alloc_endpoint_out(
95        &mut self,
96        ep_type: EndpointType,
97        ep_addr: Option<EndpointAddress>,
98        max_packet_size: u16,
99        interval_ms: u8,
100    ) -> Result<Self::EndpointOut, driver::EndpointAllocError> {
101        self.inner
102            .alloc_endpoint(ep_type, ep_addr, max_packet_size, interval_ms)
103    }
104
105    fn start(
106        self,
107        control_max_packet_size: u16,
108    ) -> (Bus<'d, UsbInstance>, ControlPipe<'d, UsbInstance>) {
109        self.inner.start(control_max_packet_size)
110    }
111}
112
113#[cfg(feature = "usb-device-impl")]
114pub fn new_bus<'d, T: Instance>(
115    _usb: impl Peripheral<P = T> + 'd,
116    _irq: impl interrupt::typelevel::Binding<T::Interrupt, InterruptHandler<T>> + 'd,
117    _dp: impl Peripheral<P = impl DpPin<T>> + 'd,
118    _dm: impl Peripheral<P = impl DmPin<T>> + 'd,
119) -> UsbdBus<UsbInstance> {
120    init::<T>();
121
122    UsbdBus::new()
123}
124
125trait SealedInstance {}
126
127/// USB instance trait.
128#[allow(private_bounds)]
129pub trait Instance: SealedInstance + RccPeripheral + 'static {
130    /// Interrupt for this USB instance.
131    type Interrupt: interrupt::typelevel::Interrupt;
132}
133
134// Internal PHY pins
135pin_trait!(DpPin, Instance);
136pin_trait!(DmPin, Instance);
137
138foreach_interrupt!(
139    ($inst:ident, usb, $block:ident, LP, $irq:ident) => {
140        impl SealedInstance for crate::peripherals::$inst {}
141
142        impl Instance for crate::peripherals::$inst {
143            type Interrupt = crate::interrupt::typelevel::$irq;
144        }
145    };
146);