Remove embassy_hal_common::usb.
The replacement is `embassy-usb`. There's a WIP driver for stm32 USBD in #709, there's no WIP driver for stm32 USB_OTG. This means we're left without USB_OTG support for now. Reason for removing is I'm going to soon remove `embassy::io`, and USB uses it. I don't want to spend time maintaining "dead" code that is going to be removed. Volunteers welcome, either to update old USB to the new IO, or write a USB_OTG driver fo the new USB.
This commit is contained in:
parent
85c0525e01
commit
fc32b3750c
@ -12,5 +12,4 @@ embassy = { version = "0.1.0", path = "../embassy" }
|
|||||||
defmt = { version = "0.3", optional = true }
|
defmt = { version = "0.3", optional = true }
|
||||||
log = { version = "0.4.14", optional = true }
|
log = { version = "0.4.14", optional = true }
|
||||||
cortex-m = "0.7.3"
|
cortex-m = "0.7.3"
|
||||||
usb-device = "0.2.8"
|
|
||||||
num-traits = { version = "0.2.14", default-features = false }
|
num-traits = { version = "0.2.14", default-features = false }
|
||||||
|
@ -10,7 +10,6 @@ mod macros;
|
|||||||
pub mod peripheral;
|
pub mod peripheral;
|
||||||
pub mod ratio;
|
pub mod ratio;
|
||||||
pub mod ring_buffer;
|
pub mod ring_buffer;
|
||||||
pub mod usb;
|
|
||||||
|
|
||||||
/// Low power blocking wait loop using WFE/SEV.
|
/// Low power blocking wait loop using WFE/SEV.
|
||||||
pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) {
|
pub fn low_power_wait_until(mut condition: impl FnMut() -> bool) {
|
||||||
|
@ -1,338 +0,0 @@
|
|||||||
// Copied from https://github.com/mvirkkunen/usbd-serial
|
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use core::convert::TryInto;
|
|
||||||
use core::mem;
|
|
||||||
use usb_device::class_prelude::*;
|
|
||||||
use usb_device::Result;
|
|
||||||
|
|
||||||
/// This should be used as `device_class` when building the `UsbDevice`.
|
|
||||||
pub const USB_CLASS_CDC: u8 = 0x02;
|
|
||||||
|
|
||||||
const USB_CLASS_CDC_DATA: u8 = 0x0a;
|
|
||||||
const CDC_SUBCLASS_ACM: u8 = 0x02;
|
|
||||||
const CDC_PROTOCOL_NONE: u8 = 0x00;
|
|
||||||
|
|
||||||
const CS_INTERFACE: u8 = 0x24;
|
|
||||||
const CDC_TYPE_HEADER: u8 = 0x00;
|
|
||||||
const CDC_TYPE_CALL_MANAGEMENT: u8 = 0x01;
|
|
||||||
const CDC_TYPE_ACM: u8 = 0x02;
|
|
||||||
const CDC_TYPE_UNION: u8 = 0x06;
|
|
||||||
|
|
||||||
const REQ_SEND_ENCAPSULATED_COMMAND: u8 = 0x00;
|
|
||||||
#[allow(unused)]
|
|
||||||
const REQ_GET_ENCAPSULATED_COMMAND: u8 = 0x01;
|
|
||||||
const REQ_SET_LINE_CODING: u8 = 0x20;
|
|
||||||
const REQ_GET_LINE_CODING: u8 = 0x21;
|
|
||||||
const REQ_SET_CONTROL_LINE_STATE: u8 = 0x22;
|
|
||||||
|
|
||||||
/// Packet level implementation of a CDC-ACM serial port.
|
|
||||||
///
|
|
||||||
/// This class can be used directly and it has the least overhead due to directly reading and
|
|
||||||
/// writing USB packets with no intermediate buffers, but it will not act like a stream-like serial
|
|
||||||
/// port. The following constraints must be followed if you use this class directly:
|
|
||||||
///
|
|
||||||
/// - `read_packet` must be called with a buffer large enough to hold max_packet_size bytes, and the
|
|
||||||
/// method will return a `WouldBlock` error if there is no packet to be read.
|
|
||||||
/// - `write_packet` must not be called with a buffer larger than max_packet_size bytes, and the
|
|
||||||
/// method will return a `WouldBlock` error if the previous packet has not been sent yet.
|
|
||||||
/// - If you write a packet that is exactly max_packet_size bytes long, it won't be processed by the
|
|
||||||
/// host operating system until a subsequent shorter packet is sent. A zero-length packet (ZLP)
|
|
||||||
/// can be sent if there is no other data to send. This is because USB bulk transactions must be
|
|
||||||
/// terminated with a short packet, even if the bulk endpoint is used for stream-like data.
|
|
||||||
pub struct CdcAcmClass<'a, B: UsbBus> {
|
|
||||||
comm_if: InterfaceNumber,
|
|
||||||
comm_ep: EndpointIn<'a, B>,
|
|
||||||
data_if: InterfaceNumber,
|
|
||||||
read_ep: EndpointOut<'a, B>,
|
|
||||||
write_ep: EndpointIn<'a, B>,
|
|
||||||
line_coding: LineCoding,
|
|
||||||
dtr: bool,
|
|
||||||
rts: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: UsbBus> CdcAcmClass<'_, B> {
|
|
||||||
/// Creates a new CdcAcmClass with the provided UsbBus and max_packet_size in bytes. For
|
|
||||||
/// full-speed devices, max_packet_size has to be one of 8, 16, 32 or 64.
|
|
||||||
pub fn new(alloc: &UsbBusAllocator<B>, max_packet_size: u16) -> CdcAcmClass<'_, B> {
|
|
||||||
CdcAcmClass {
|
|
||||||
comm_if: alloc.interface(),
|
|
||||||
comm_ep: alloc.interrupt(8, 255),
|
|
||||||
data_if: alloc.interface(),
|
|
||||||
read_ep: alloc.bulk(max_packet_size),
|
|
||||||
write_ep: alloc.bulk(max_packet_size),
|
|
||||||
line_coding: LineCoding {
|
|
||||||
stop_bits: StopBits::One,
|
|
||||||
data_bits: 8,
|
|
||||||
parity_type: ParityType::None,
|
|
||||||
data_rate: 8_000,
|
|
||||||
},
|
|
||||||
dtr: false,
|
|
||||||
rts: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the maximum packet size in bytes.
|
|
||||||
pub fn max_packet_size(&self) -> u16 {
|
|
||||||
// The size is the same for both endpoints.
|
|
||||||
self.read_ep.max_packet_size()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the current line coding. The line coding contains information that's mainly relevant
|
|
||||||
/// for USB to UART serial port emulators, and can be ignored if not relevant.
|
|
||||||
pub fn line_coding(&self) -> &LineCoding {
|
|
||||||
&self.line_coding
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the DTR (data terminal ready) state
|
|
||||||
pub fn dtr(&self) -> bool {
|
|
||||||
self.dtr
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the RTS (request to send) state
|
|
||||||
pub fn rts(&self) -> bool {
|
|
||||||
self.rts
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Writes a single packet into the IN endpoint.
|
|
||||||
pub fn write_packet(&mut self, data: &[u8]) -> Result<usize> {
|
|
||||||
self.write_ep.write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads a single packet from the OUT endpoint.
|
|
||||||
pub fn read_packet(&mut self, data: &mut [u8]) -> Result<usize> {
|
|
||||||
self.read_ep.read(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the address of the IN endpoint.
|
|
||||||
pub fn write_ep_address(&self) -> EndpointAddress {
|
|
||||||
self.write_ep.address()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the address of the OUT endpoint.
|
|
||||||
pub fn read_ep_address(&self) -> EndpointAddress {
|
|
||||||
self.read_ep.address()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B: UsbBus> UsbClass<B> for CdcAcmClass<'_, B> {
|
|
||||||
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<()> {
|
|
||||||
writer.iad(
|
|
||||||
self.comm_if,
|
|
||||||
2,
|
|
||||||
USB_CLASS_CDC,
|
|
||||||
CDC_SUBCLASS_ACM,
|
|
||||||
CDC_PROTOCOL_NONE,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.interface(
|
|
||||||
self.comm_if,
|
|
||||||
USB_CLASS_CDC,
|
|
||||||
CDC_SUBCLASS_ACM,
|
|
||||||
CDC_PROTOCOL_NONE,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.write(
|
|
||||||
CS_INTERFACE,
|
|
||||||
&[
|
|
||||||
CDC_TYPE_HEADER, // bDescriptorSubtype
|
|
||||||
0x10,
|
|
||||||
0x01, // bcdCDC (1.10)
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.write(
|
|
||||||
CS_INTERFACE,
|
|
||||||
&[
|
|
||||||
CDC_TYPE_ACM, // bDescriptorSubtype
|
|
||||||
0x00, // bmCapabilities
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.write(
|
|
||||||
CS_INTERFACE,
|
|
||||||
&[
|
|
||||||
CDC_TYPE_UNION, // bDescriptorSubtype
|
|
||||||
self.comm_if.into(), // bControlInterface
|
|
||||||
self.data_if.into(), // bSubordinateInterface
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.write(
|
|
||||||
CS_INTERFACE,
|
|
||||||
&[
|
|
||||||
CDC_TYPE_CALL_MANAGEMENT, // bDescriptorSubtype
|
|
||||||
0x00, // bmCapabilities
|
|
||||||
self.data_if.into(), // bDataInterface
|
|
||||||
],
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.endpoint(&self.comm_ep)?;
|
|
||||||
|
|
||||||
writer.interface(self.data_if, USB_CLASS_CDC_DATA, 0x00, 0x00)?;
|
|
||||||
|
|
||||||
writer.endpoint(&self.write_ep)?;
|
|
||||||
writer.endpoint(&self.read_ep)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) {
|
|
||||||
self.line_coding = LineCoding::default();
|
|
||||||
self.dtr = false;
|
|
||||||
self.rts = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn control_in(&mut self, xfer: ControlIn<B>) {
|
|
||||||
let req = xfer.request();
|
|
||||||
|
|
||||||
if !(req.request_type == control::RequestType::Class
|
|
||||||
&& req.recipient == control::Recipient::Interface
|
|
||||||
&& req.index == u8::from(self.comm_if) as u16)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match req.request {
|
|
||||||
// REQ_GET_ENCAPSULATED_COMMAND is not really supported - it will be rejected below.
|
|
||||||
REQ_GET_LINE_CODING if req.length == 7 => {
|
|
||||||
xfer.accept(|data| {
|
|
||||||
data[0..4].copy_from_slice(&self.line_coding.data_rate.to_le_bytes());
|
|
||||||
data[4] = self.line_coding.stop_bits as u8;
|
|
||||||
data[5] = self.line_coding.parity_type as u8;
|
|
||||||
data[6] = self.line_coding.data_bits;
|
|
||||||
|
|
||||||
Ok(7)
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
xfer.reject().ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn control_out(&mut self, xfer: ControlOut<B>) {
|
|
||||||
let req = xfer.request();
|
|
||||||
|
|
||||||
if !(req.request_type == control::RequestType::Class
|
|
||||||
&& req.recipient == control::Recipient::Interface
|
|
||||||
&& req.index == u8::from(self.comm_if) as u16)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
match req.request {
|
|
||||||
REQ_SEND_ENCAPSULATED_COMMAND => {
|
|
||||||
// We don't actually support encapsulated commands but pretend we do for standards
|
|
||||||
// compatibility.
|
|
||||||
xfer.accept().ok();
|
|
||||||
}
|
|
||||||
REQ_SET_LINE_CODING if xfer.data().len() >= 7 => {
|
|
||||||
self.line_coding.data_rate =
|
|
||||||
u32::from_le_bytes(xfer.data()[0..4].try_into().unwrap());
|
|
||||||
self.line_coding.stop_bits = xfer.data()[4].into();
|
|
||||||
self.line_coding.parity_type = xfer.data()[5].into();
|
|
||||||
self.line_coding.data_bits = xfer.data()[6];
|
|
||||||
|
|
||||||
xfer.accept().ok();
|
|
||||||
}
|
|
||||||
REQ_SET_CONTROL_LINE_STATE => {
|
|
||||||
self.dtr = (req.value & 0x0001) != 0;
|
|
||||||
self.rts = (req.value & 0x0002) != 0;
|
|
||||||
|
|
||||||
xfer.accept().ok();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
xfer.reject().ok();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Number of stop bits for LineCoding
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum StopBits {
|
|
||||||
/// 1 stop bit
|
|
||||||
One = 0,
|
|
||||||
|
|
||||||
/// 1.5 stop bits
|
|
||||||
OnePointFive = 1,
|
|
||||||
|
|
||||||
/// 2 stop bits
|
|
||||||
Two = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u8> for StopBits {
|
|
||||||
fn from(value: u8) -> Self {
|
|
||||||
if value <= 2 {
|
|
||||||
unsafe { mem::transmute(value) }
|
|
||||||
} else {
|
|
||||||
StopBits::One
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parity for LineCoding
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum ParityType {
|
|
||||||
None = 0,
|
|
||||||
Odd = 1,
|
|
||||||
Event = 2,
|
|
||||||
Mark = 3,
|
|
||||||
Space = 4,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u8> for ParityType {
|
|
||||||
fn from(value: u8) -> Self {
|
|
||||||
if value <= 4 {
|
|
||||||
unsafe { mem::transmute(value) }
|
|
||||||
} else {
|
|
||||||
ParityType::None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Line coding parameters
|
|
||||||
///
|
|
||||||
/// This is provided by the host for specifying the standard UART parameters such as baud rate. Can
|
|
||||||
/// be ignored if you don't plan to interface with a physical UART.
|
|
||||||
pub struct LineCoding {
|
|
||||||
stop_bits: StopBits,
|
|
||||||
data_bits: u8,
|
|
||||||
parity_type: ParityType,
|
|
||||||
data_rate: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl LineCoding {
|
|
||||||
/// Gets the number of stop bits for UART communication.
|
|
||||||
pub fn stop_bits(&self) -> StopBits {
|
|
||||||
self.stop_bits
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the number of data bits for UART communication.
|
|
||||||
pub fn data_bits(&self) -> u8 {
|
|
||||||
self.data_bits
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the parity type for UART communication.
|
|
||||||
pub fn parity_type(&self) -> ParityType {
|
|
||||||
self.parity_type
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the data rate in bits per second for UART communication.
|
|
||||||
pub fn data_rate(&self) -> u32 {
|
|
||||||
self.data_rate
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for LineCoding {
|
|
||||||
fn default() -> Self {
|
|
||||||
LineCoding {
|
|
||||||
stop_bits: StopBits::One,
|
|
||||||
data_bits: 8,
|
|
||||||
parity_type: ParityType::None,
|
|
||||||
data_rate: 8_000,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,267 +0,0 @@
|
|||||||
use core::cell::RefCell;
|
|
||||||
use core::marker::PhantomData;
|
|
||||||
use core::pin::Pin;
|
|
||||||
|
|
||||||
use usb_device::bus::UsbBus;
|
|
||||||
use usb_device::class::UsbClass;
|
|
||||||
use usb_device::device::UsbDevice;
|
|
||||||
|
|
||||||
mod cdc_acm;
|
|
||||||
pub mod usb_serial;
|
|
||||||
|
|
||||||
use crate::peripheral::{PeripheralMutex, PeripheralState, StateStorage};
|
|
||||||
use embassy::interrupt::Interrupt;
|
|
||||||
pub use usb_serial::{ReadInterface, UsbSerial, WriteInterface};
|
|
||||||
|
|
||||||
/// Marker trait to mark an interrupt to be used with the [`Usb`] abstraction.
|
|
||||||
pub unsafe trait USBInterrupt: Interrupt + Send {}
|
|
||||||
|
|
||||||
pub struct State<'bus, B, T, I>(StateStorage<StateInner<'bus, B, T, I>>)
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt;
|
|
||||||
|
|
||||||
impl<'bus, B, T, I> State<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self(StateStorage::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct StateInner<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
device: UsbDevice<'bus, B>,
|
|
||||||
pub(crate) classes: T,
|
|
||||||
_interrupt: PhantomData<I>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Usb<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
// Don't you dare moving out `PeripheralMutex`
|
|
||||||
inner: RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, I>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, B, T, I> Usb<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
/// safety: the returned instance is not leak-safe
|
|
||||||
pub unsafe fn new<S: IntoClassSet<B, T>>(
|
|
||||||
state: &'bus mut State<'bus, B, T, I>,
|
|
||||||
device: UsbDevice<'bus, B>,
|
|
||||||
class_set: S,
|
|
||||||
irq: I,
|
|
||||||
) -> Self {
|
|
||||||
let mutex = PeripheralMutex::new_unchecked(irq, &mut state.0, || StateInner {
|
|
||||||
device,
|
|
||||||
classes: class_set.into_class_set(),
|
|
||||||
_interrupt: PhantomData,
|
|
||||||
});
|
|
||||||
Self {
|
|
||||||
inner: RefCell::new(mutex),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B> + SerialState<'bus, 'c, B, Index0>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
/// Take a serial class that was passed as the first class in a tuple
|
|
||||||
pub fn take_serial_0<'a>(
|
|
||||||
self: Pin<&'a Self>,
|
|
||||||
) -> (
|
|
||||||
ReadInterface<'a, 'bus, 'c, Index0, B, T, I>,
|
|
||||||
WriteInterface<'a, 'bus, 'c, Index0, B, T, I>,
|
|
||||||
) {
|
|
||||||
let this = self.get_ref();
|
|
||||||
|
|
||||||
let r = ReadInterface {
|
|
||||||
inner: &this.inner,
|
|
||||||
_buf_lifetime: PhantomData,
|
|
||||||
_index: PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
let w = WriteInterface {
|
|
||||||
inner: &this.inner,
|
|
||||||
_buf_lifetime: PhantomData,
|
|
||||||
_index: PhantomData,
|
|
||||||
};
|
|
||||||
(r, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'c, B, T, I> Usb<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B> + SerialState<'bus, 'c, B, Index1>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
/// Take a serial class that was passed as the second class in a tuple
|
|
||||||
pub fn take_serial_1<'a>(
|
|
||||||
self: Pin<&'a Self>,
|
|
||||||
) -> (
|
|
||||||
ReadInterface<'a, 'bus, 'c, Index1, B, T, I>,
|
|
||||||
WriteInterface<'a, 'bus, 'c, Index1, B, T, I>,
|
|
||||||
) {
|
|
||||||
let this = self.get_ref();
|
|
||||||
|
|
||||||
let r = ReadInterface {
|
|
||||||
inner: &this.inner,
|
|
||||||
_buf_lifetime: PhantomData,
|
|
||||||
_index: PhantomData,
|
|
||||||
};
|
|
||||||
|
|
||||||
let w = WriteInterface {
|
|
||||||
inner: &this.inner,
|
|
||||||
_buf_lifetime: PhantomData,
|
|
||||||
_index: PhantomData,
|
|
||||||
};
|
|
||||||
(r, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, B, T, I> PeripheralState for StateInner<'bus, B, T, I>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
T: ClassSet<B>,
|
|
||||||
I: USBInterrupt,
|
|
||||||
{
|
|
||||||
type Interrupt = I;
|
|
||||||
fn on_interrupt(&mut self) {
|
|
||||||
self.classes.poll_all(&mut self.device);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ClassSet<B: UsbBus>: Send {
|
|
||||||
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait IntoClassSet<B: UsbBus, C: ClassSet<B>> {
|
|
||||||
fn into_class_set(self) -> C;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ClassSet1<B, C1>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
C1: UsbClass<B>,
|
|
||||||
{
|
|
||||||
class: C1,
|
|
||||||
_bus: PhantomData<B>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ClassSet2<B, C1, C2>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
C1: UsbClass<B>,
|
|
||||||
C2: UsbClass<B>,
|
|
||||||
{
|
|
||||||
class1: C1,
|
|
||||||
class2: C2,
|
|
||||||
_bus: PhantomData<B>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The first class into a [`ClassSet`]
|
|
||||||
pub struct Index0;
|
|
||||||
|
|
||||||
/// The second class into a [`ClassSet`]
|
|
||||||
pub struct Index1;
|
|
||||||
|
|
||||||
impl<B, C1> ClassSet<B> for ClassSet1<B, C1>
|
|
||||||
where
|
|
||||||
B: UsbBus + Send,
|
|
||||||
C1: UsbClass<B> + Send,
|
|
||||||
{
|
|
||||||
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool {
|
|
||||||
device.poll(&mut [&mut self.class])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, C1, C2> ClassSet<B> for ClassSet2<B, C1, C2>
|
|
||||||
where
|
|
||||||
B: UsbBus + Send,
|
|
||||||
C1: UsbClass<B> + Send,
|
|
||||||
C2: UsbClass<B> + Send,
|
|
||||||
{
|
|
||||||
fn poll_all(&mut self, device: &mut UsbDevice<'_, B>) -> bool {
|
|
||||||
device.poll(&mut [&mut self.class1, &mut self.class2])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, C1> IntoClassSet<B, ClassSet1<B, C1>> for C1
|
|
||||||
where
|
|
||||||
B: UsbBus + Send,
|
|
||||||
C1: UsbClass<B> + Send,
|
|
||||||
{
|
|
||||||
fn into_class_set(self) -> ClassSet1<B, C1> {
|
|
||||||
ClassSet1 {
|
|
||||||
class: self,
|
|
||||||
_bus: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B, C1, C2> IntoClassSet<B, ClassSet2<B, C1, C2>> for (C1, C2)
|
|
||||||
where
|
|
||||||
B: UsbBus + Send,
|
|
||||||
C1: UsbClass<B> + Send,
|
|
||||||
C2: UsbClass<B> + Send,
|
|
||||||
{
|
|
||||||
fn into_class_set(self) -> ClassSet2<B, C1, C2> {
|
|
||||||
ClassSet2 {
|
|
||||||
class1: self.0,
|
|
||||||
class2: self.1,
|
|
||||||
_bus: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait for a USB State that has a serial class inside
|
|
||||||
pub trait SerialState<'bus, 'a, B: UsbBus, I> {
|
|
||||||
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B: UsbBus> SerialState<'bus, 'a, B, Index0>
|
|
||||||
for ClassSet1<B, UsbSerial<'bus, 'a, B>>
|
|
||||||
{
|
|
||||||
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> {
|
|
||||||
&mut self.class
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B, C2> SerialState<'bus, 'a, B, Index0> for ClassSet2<B, UsbSerial<'bus, 'a, B>, C2>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
C2: UsbClass<B>,
|
|
||||||
{
|
|
||||||
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> {
|
|
||||||
&mut self.class1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B, C1> SerialState<'bus, 'a, B, Index1> for ClassSet2<B, C1, UsbSerial<'bus, 'a, B>>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
C1: UsbClass<B>,
|
|
||||||
{
|
|
||||||
fn get_serial(&mut self) -> &mut UsbSerial<'bus, 'a, B> {
|
|
||||||
&mut self.class2
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,345 +0,0 @@
|
|||||||
use core::cell::RefCell;
|
|
||||||
use core::marker::{PhantomData, Unpin};
|
|
||||||
use core::pin::Pin;
|
|
||||||
use core::task::{Context, Poll};
|
|
||||||
|
|
||||||
use embassy::io::{self, AsyncBufRead, AsyncWrite};
|
|
||||||
use embassy::waitqueue::WakerRegistration;
|
|
||||||
use usb_device::bus::UsbBus;
|
|
||||||
use usb_device::class_prelude::*;
|
|
||||||
use usb_device::UsbError;
|
|
||||||
|
|
||||||
use super::cdc_acm::CdcAcmClass;
|
|
||||||
use super::StateInner;
|
|
||||||
use crate::peripheral::PeripheralMutex;
|
|
||||||
use crate::ring_buffer::RingBuffer;
|
|
||||||
use crate::usb::{ClassSet, SerialState, USBInterrupt};
|
|
||||||
|
|
||||||
pub struct ReadInterface<'a, 'bus, 'c, I, B, T, INT>
|
|
||||||
where
|
|
||||||
I: Unpin,
|
|
||||||
B: UsbBus,
|
|
||||||
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
|
|
||||||
INT: USBInterrupt,
|
|
||||||
{
|
|
||||||
// Don't you dare moving out `PeripheralMutex`
|
|
||||||
pub(crate) inner: &'a RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, INT>>>,
|
|
||||||
pub(crate) _buf_lifetime: PhantomData<&'c T>,
|
|
||||||
pub(crate) _index: PhantomData<I>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Write interface for USB CDC_ACM
|
|
||||||
///
|
|
||||||
/// This interface is buffered, meaning that after the write returns the bytes might not be fully
|
|
||||||
/// on the wire just yet
|
|
||||||
pub struct WriteInterface<'a, 'bus, 'c, I, B, T, INT>
|
|
||||||
where
|
|
||||||
I: Unpin,
|
|
||||||
B: UsbBus,
|
|
||||||
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
|
|
||||||
INT: USBInterrupt,
|
|
||||||
{
|
|
||||||
// Don't you dare moving out `PeripheralMutex`
|
|
||||||
pub(crate) inner: &'a RefCell<PeripheralMutex<'bus, StateInner<'bus, B, T, INT>>>,
|
|
||||||
pub(crate) _buf_lifetime: PhantomData<&'c T>,
|
|
||||||
pub(crate) _index: PhantomData<I>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'bus, 'c, I, B, T, INT> AsyncBufRead for ReadInterface<'a, 'bus, 'c, I, B, T, INT>
|
|
||||||
where
|
|
||||||
I: Unpin,
|
|
||||||
B: UsbBus,
|
|
||||||
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
|
|
||||||
INT: USBInterrupt,
|
|
||||||
{
|
|
||||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
let mut mutex = this.inner.borrow_mut();
|
|
||||||
mutex.with(|state| {
|
|
||||||
let serial = state.classes.get_serial();
|
|
||||||
let serial = Pin::new(serial);
|
|
||||||
|
|
||||||
match serial.poll_fill_buf(cx) {
|
|
||||||
Poll::Ready(Ok(buf)) => {
|
|
||||||
let buf: &[u8] = buf;
|
|
||||||
// NOTE(unsafe) This part of the buffer won't be modified until the user calls
|
|
||||||
// consume, which will invalidate this ref
|
|
||||||
let buf: &[u8] = unsafe { core::mem::transmute(buf) };
|
|
||||||
Poll::Ready(Ok(buf))
|
|
||||||
}
|
|
||||||
Poll::Ready(Err(_)) => Poll::Ready(Err(io::Error::Other)),
|
|
||||||
Poll::Pending => Poll::Pending,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
||||||
let this = self.get_mut();
|
|
||||||
let mut mutex = this.inner.borrow_mut();
|
|
||||||
mutex.with(|state| {
|
|
||||||
let serial = state.classes.get_serial();
|
|
||||||
let serial = Pin::new(serial);
|
|
||||||
|
|
||||||
serial.consume(amt);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'bus, 'c, I, B, T, INT> AsyncWrite for WriteInterface<'a, 'bus, 'c, I, B, T, INT>
|
|
||||||
where
|
|
||||||
I: Unpin,
|
|
||||||
B: UsbBus,
|
|
||||||
T: SerialState<'bus, 'c, B, I> + ClassSet<B>,
|
|
||||||
INT: USBInterrupt,
|
|
||||||
{
|
|
||||||
fn poll_write(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<io::Result<usize>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
let mut mutex = this.inner.borrow_mut();
|
|
||||||
mutex.with(|state| {
|
|
||||||
let serial = state.classes.get_serial();
|
|
||||||
let serial = Pin::new(serial);
|
|
||||||
|
|
||||||
serial.poll_write(cx, buf)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
let mut mutex = this.inner.borrow_mut();
|
|
||||||
mutex.with(|state| {
|
|
||||||
let serial = state.classes.get_serial();
|
|
||||||
let serial = Pin::new(serial);
|
|
||||||
|
|
||||||
serial.poll_flush(cx)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UsbSerial<'bus, 'a, B: UsbBus> {
|
|
||||||
inner: CdcAcmClass<'bus, B>,
|
|
||||||
read_buf: RingBuffer<'a>,
|
|
||||||
write_buf: RingBuffer<'a>,
|
|
||||||
read_waker: WakerRegistration,
|
|
||||||
write_waker: WakerRegistration,
|
|
||||||
write_state: WriteState,
|
|
||||||
read_error: bool,
|
|
||||||
write_error: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B: UsbBus> AsyncBufRead for UsbSerial<'bus, 'a, B> {
|
|
||||||
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&[u8]>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
|
|
||||||
if this.read_error {
|
|
||||||
this.read_error = false;
|
|
||||||
return Poll::Ready(Err(io::Error::Other));
|
|
||||||
}
|
|
||||||
|
|
||||||
let buf = this.read_buf.pop_buf();
|
|
||||||
if buf.is_empty() {
|
|
||||||
this.read_waker.register(cx.waker());
|
|
||||||
return Poll::Pending;
|
|
||||||
}
|
|
||||||
Poll::Ready(Ok(buf))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn consume(self: Pin<&mut Self>, amt: usize) {
|
|
||||||
self.get_mut().read_buf.pop(amt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B: UsbBus> AsyncWrite for UsbSerial<'bus, 'a, B> {
|
|
||||||
fn poll_write(
|
|
||||||
self: Pin<&mut Self>,
|
|
||||||
cx: &mut Context<'_>,
|
|
||||||
buf: &[u8],
|
|
||||||
) -> Poll<io::Result<usize>> {
|
|
||||||
let this = self.get_mut();
|
|
||||||
|
|
||||||
if this.write_error {
|
|
||||||
this.write_error = false;
|
|
||||||
return Poll::Ready(Err(io::Error::Other));
|
|
||||||
}
|
|
||||||
|
|
||||||
let write_buf = this.write_buf.push_buf();
|
|
||||||
if write_buf.is_empty() {
|
|
||||||
trace!("buf full, registering waker");
|
|
||||||
this.write_waker.register(cx.waker());
|
|
||||||
return Poll::Pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
let count = write_buf.len().min(buf.len());
|
|
||||||
write_buf[..count].copy_from_slice(&buf[..count]);
|
|
||||||
this.write_buf.push(count);
|
|
||||||
|
|
||||||
this.flush_write();
|
|
||||||
Poll::Ready(Ok(count))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
|
|
||||||
Poll::Ready(Ok(()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Keeps track of the type of the last written packet.
|
|
||||||
enum WriteState {
|
|
||||||
/// No packets in-flight
|
|
||||||
Idle,
|
|
||||||
|
|
||||||
/// Short packet currently in-flight
|
|
||||||
Short,
|
|
||||||
|
|
||||||
/// Full packet current in-flight. A full packet must be followed by a short packet for the host
|
|
||||||
/// OS to see the transaction. The data is the number of subsequent full packets sent so far. A
|
|
||||||
/// short packet is forced every SHORT_PACKET_INTERVAL packets so that the OS sees data in a
|
|
||||||
/// timely manner.
|
|
||||||
Full(usize),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'bus, 'a, B: UsbBus> UsbSerial<'bus, 'a, B> {
|
|
||||||
pub fn new(
|
|
||||||
alloc: &'bus UsbBusAllocator<B>,
|
|
||||||
read_buf: &'a mut [u8],
|
|
||||||
write_buf: &'a mut [u8],
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
inner: CdcAcmClass::new(alloc, 64),
|
|
||||||
read_buf: RingBuffer::new(read_buf),
|
|
||||||
write_buf: RingBuffer::new(write_buf),
|
|
||||||
read_waker: WakerRegistration::new(),
|
|
||||||
write_waker: WakerRegistration::new(),
|
|
||||||
write_state: WriteState::Idle,
|
|
||||||
read_error: false,
|
|
||||||
write_error: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flush_write(&mut self) {
|
|
||||||
/// If this many full size packets have been sent in a row, a short packet will be sent so that the
|
|
||||||
/// host sees the data in a timely manner.
|
|
||||||
const SHORT_PACKET_INTERVAL: usize = 10;
|
|
||||||
|
|
||||||
let full_size_packets = match self.write_state {
|
|
||||||
WriteState::Full(c) => c,
|
|
||||||
_ => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let ep_size = self.inner.max_packet_size() as usize;
|
|
||||||
let max_size = if full_size_packets > SHORT_PACKET_INTERVAL {
|
|
||||||
ep_size - 1
|
|
||||||
} else {
|
|
||||||
ep_size
|
|
||||||
};
|
|
||||||
|
|
||||||
let buf = {
|
|
||||||
let buf = self.write_buf.pop_buf();
|
|
||||||
if buf.len() > max_size {
|
|
||||||
&buf[..max_size]
|
|
||||||
} else {
|
|
||||||
buf
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if !buf.is_empty() {
|
|
||||||
trace!("writing packet len {}", buf.len());
|
|
||||||
let count = match self.inner.write_packet(buf) {
|
|
||||||
Ok(c) => {
|
|
||||||
trace!("write packet: OK {}", c);
|
|
||||||
c
|
|
||||||
}
|
|
||||||
Err(UsbError::WouldBlock) => {
|
|
||||||
trace!("write packet: WouldBlock");
|
|
||||||
0
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
trace!("write packet: error");
|
|
||||||
self.write_error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if buf.len() == ep_size {
|
|
||||||
self.write_state = WriteState::Full(full_size_packets + 1);
|
|
||||||
} else {
|
|
||||||
self.write_state = WriteState::Short;
|
|
||||||
}
|
|
||||||
self.write_buf.pop(count);
|
|
||||||
} else if full_size_packets > 0 {
|
|
||||||
trace!("writing empty packet");
|
|
||||||
match self.inner.write_packet(&[]) {
|
|
||||||
Ok(_) => {
|
|
||||||
trace!("write empty packet: OK");
|
|
||||||
}
|
|
||||||
Err(UsbError::WouldBlock) => {
|
|
||||||
trace!("write empty packet: WouldBlock");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Err(_) => {
|
|
||||||
trace!("write empty packet: Error");
|
|
||||||
self.write_error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.write_state = WriteState::Idle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<B> UsbClass<B> for UsbSerial<'_, '_, B>
|
|
||||||
where
|
|
||||||
B: UsbBus,
|
|
||||||
{
|
|
||||||
fn get_configuration_descriptors(&self, writer: &mut DescriptorWriter) -> Result<(), UsbError> {
|
|
||||||
self.inner.get_configuration_descriptors(writer)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) {
|
|
||||||
self.inner.reset();
|
|
||||||
self.read_buf.clear();
|
|
||||||
self.write_buf.clear();
|
|
||||||
self.write_state = WriteState::Idle;
|
|
||||||
self.read_waker.wake();
|
|
||||||
self.write_waker.wake();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn endpoint_in_complete(&mut self, addr: EndpointAddress) {
|
|
||||||
trace!("DONE endpoint_in_complete");
|
|
||||||
if addr == self.inner.write_ep_address() {
|
|
||||||
trace!("DONE writing packet, waking");
|
|
||||||
self.write_waker.wake();
|
|
||||||
|
|
||||||
self.flush_write();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn endpoint_out(&mut self, addr: EndpointAddress) {
|
|
||||||
if addr == self.inner.read_ep_address() {
|
|
||||||
let buf = self.read_buf.push_buf();
|
|
||||||
let count = match self.inner.read_packet(buf) {
|
|
||||||
Ok(c) => c,
|
|
||||||
Err(UsbError::WouldBlock) => 0,
|
|
||||||
Err(_) => {
|
|
||||||
self.read_error = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if count > 0 {
|
|
||||||
self.read_buf.push(count);
|
|
||||||
self.read_waker.wake();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn control_in(&mut self, xfer: ControlIn<B>) {
|
|
||||||
self.inner.control_in(xfer);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn control_out(&mut self, xfer: ControlOut<B>) {
|
|
||||||
self.inner.control_out(xfer);
|
|
||||||
}
|
|
||||||
}
|
|
@ -53,7 +53,6 @@ futures = { version = "0.3.17", default-features = false, features = ["async-awa
|
|||||||
rand_core = "0.6.3"
|
rand_core = "0.6.3"
|
||||||
sdio-host = "0.5.0"
|
sdio-host = "0.5.0"
|
||||||
embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true }
|
embedded-sdmmc = { git = "https://github.com/thalesfragoso/embedded-sdmmc-rs", branch = "async", optional = true }
|
||||||
synopsys-usb-otg = { version = "0.3", features = ["cortex-m", "hs"], optional = true }
|
|
||||||
critical-section = "0.2.5"
|
critical-section = "0.2.5"
|
||||||
bare-metal = "1.0.0"
|
bare-metal = "1.0.0"
|
||||||
atomic-polyfill = "0.1.5"
|
atomic-polyfill = "0.1.5"
|
||||||
@ -76,7 +75,6 @@ net = ["embassy-net", "vcell"]
|
|||||||
memory-x = ["stm32-metapac/memory-x"]
|
memory-x = ["stm32-metapac/memory-x"]
|
||||||
subghz = []
|
subghz = []
|
||||||
exti = []
|
exti = []
|
||||||
usb-otg = ["synopsys-usb-otg"]
|
|
||||||
|
|
||||||
# Features starting with `_` are for internal use only. They're not intended
|
# Features starting with `_` are for internal use only. They're not intended
|
||||||
# to be enabled by other crates, and are not covered by semver guarantees.
|
# to be enabled by other crates, and are not covered by semver guarantees.
|
||||||
|
@ -279,22 +279,22 @@ fn main() {
|
|||||||
(("dcmi", "HSYNC"), (quote!(crate::dcmi::HSyncPin), quote!())),
|
(("dcmi", "HSYNC"), (quote!(crate::dcmi::HSyncPin), quote!())),
|
||||||
(("dcmi", "VSYNC"), (quote!(crate::dcmi::VSyncPin), quote!())),
|
(("dcmi", "VSYNC"), (quote!(crate::dcmi::VSyncPin), quote!())),
|
||||||
(("dcmi", "PIXCLK"), (quote!(crate::dcmi::PixClkPin), quote!())),
|
(("dcmi", "PIXCLK"), (quote!(crate::dcmi::PixClkPin), quote!())),
|
||||||
(("otgfs", "DP"), (quote!(crate::usb_otg::DpPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otgfs", "DP"), (quote!(crate::usb_otg::DpPin), quote!())),
|
||||||
(("otgfs", "DM"), (quote!(crate::usb_otg::DmPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otgfs", "DM"), (quote!(crate::usb_otg::DmPin), quote!())),
|
||||||
(("otghs", "DP"), (quote!(crate::usb_otg::DpPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "DP"), (quote!(crate::usb_otg::DpPin), quote!())),
|
||||||
(("otghs", "DM"), (quote!(crate::usb_otg::DmPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "DM"), (quote!(crate::usb_otg::DmPin), quote!())),
|
||||||
(("otghs", "ULPI_CK"), (quote!(crate::usb_otg::UlpiClkPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_CK"), (quote!(crate::usb_otg::UlpiClkPin), quote!())),
|
||||||
(("otghs", "ULPI_DIR"), (quote!(crate::usb_otg::UlpiDirPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_DIR"), (quote!(crate::usb_otg::UlpiDirPin), quote!())),
|
||||||
(("otghs", "ULPI_NXT"), (quote!(crate::usb_otg::UlpiNxtPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_NXT"), (quote!(crate::usb_otg::UlpiNxtPin), quote!())),
|
||||||
(("otghs", "ULPI_STP"), (quote!(crate::usb_otg::UlpiStpPin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_STP"), (quote!(crate::usb_otg::UlpiStpPin), quote!())),
|
||||||
(("otghs", "ULPI_D0"), (quote!(crate::usb_otg::UlpiD0Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D0"), (quote!(crate::usb_otg::UlpiD0Pin), quote!())),
|
||||||
(("otghs", "ULPI_D1"), (quote!(crate::usb_otg::UlpiD1Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D1"), (quote!(crate::usb_otg::UlpiD1Pin), quote!())),
|
||||||
(("otghs", "ULPI_D2"), (quote!(crate::usb_otg::UlpiD2Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D2"), (quote!(crate::usb_otg::UlpiD2Pin), quote!())),
|
||||||
(("otghs", "ULPI_D3"), (quote!(crate::usb_otg::UlpiD3Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D3"), (quote!(crate::usb_otg::UlpiD3Pin), quote!())),
|
||||||
(("otghs", "ULPI_D4"), (quote!(crate::usb_otg::UlpiD4Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D4"), (quote!(crate::usb_otg::UlpiD4Pin), quote!())),
|
||||||
(("otghs", "ULPI_D5"), (quote!(crate::usb_otg::UlpiD5Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D5"), (quote!(crate::usb_otg::UlpiD5Pin), quote!())),
|
||||||
(("otghs", "ULPI_D6"), (quote!(crate::usb_otg::UlpiD6Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D6"), (quote!(crate::usb_otg::UlpiD6Pin), quote!())),
|
||||||
(("otghs", "ULPI_D7"), (quote!(crate::usb_otg::UlpiD7Pin), quote!(#[cfg(feature="usb-otg")]))),
|
(("otghs", "ULPI_D7"), (quote!(crate::usb_otg::UlpiD7Pin), quote!())),
|
||||||
(("can", "TX"), (quote!(crate::can::TxPin), quote!())),
|
(("can", "TX"), (quote!(crate::can::TxPin), quote!())),
|
||||||
(("can", "RX"), (quote!(crate::can::RxPin), quote!())),
|
(("can", "RX"), (quote!(crate::can::RxPin), quote!())),
|
||||||
(("eth", "REF_CLK"), (quote!(crate::eth::RefClkPin), quote!(#[cfg(feature="net")]))),
|
(("eth", "REF_CLK"), (quote!(crate::eth::RefClkPin), quote!(#[cfg(feature="net")]))),
|
||||||
|
@ -61,7 +61,7 @@ pub mod sdmmc;
|
|||||||
pub mod spi;
|
pub mod spi;
|
||||||
#[cfg(usart)]
|
#[cfg(usart)]
|
||||||
pub mod usart;
|
pub mod usart;
|
||||||
#[cfg(feature = "usb-otg")]
|
#[cfg(any(otgfs, otghs))]
|
||||||
pub mod usb_otg;
|
pub mod usb_otg;
|
||||||
|
|
||||||
#[cfg(feature = "subghz")]
|
#[cfg(feature = "subghz")]
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
use embassy::util::Unborrow;
|
use embassy::util::Unborrow;
|
||||||
use embassy_hal_common::unborrow;
|
use embassy_hal_common::unborrow;
|
||||||
use synopsys_usb_otg::{PhyType, UsbPeripheral};
|
|
||||||
|
|
||||||
use crate::gpio::sealed::AFType;
|
use crate::gpio::sealed::AFType;
|
||||||
use crate::gpio::Speed;
|
use crate::gpio::Speed;
|
||||||
use crate::{peripherals, rcc::RccPeripheral};
|
use crate::{peripherals, rcc::RccPeripheral};
|
||||||
|
|
||||||
pub use embassy_hal_common::usb::*;
|
|
||||||
pub use synopsys_usb_otg::UsbBus;
|
|
||||||
|
|
||||||
macro_rules! config_ulpi_pins {
|
macro_rules! config_ulpi_pins {
|
||||||
($($pin:ident),*) => {
|
($($pin:ident),*) => {
|
||||||
unborrow!($($pin),*);
|
unborrow!($($pin),*);
|
||||||
@ -23,9 +19,24 @@ macro_rules! config_ulpi_pins {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// USB PHY type
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
pub enum PhyType {
|
||||||
|
/// Internal Full-Speed PHY
|
||||||
|
///
|
||||||
|
/// Available on most High-Speed peripherals.
|
||||||
|
InternalFullSpeed,
|
||||||
|
/// Internal High-Speed PHY
|
||||||
|
///
|
||||||
|
/// Available on a few STM32 chips.
|
||||||
|
InternalHighSpeed,
|
||||||
|
/// External ULPI High-Speed PHY
|
||||||
|
ExternalHighSpeed,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct UsbOtg<'d, T: Instance> {
|
pub struct UsbOtg<'d, T: Instance> {
|
||||||
phantom: PhantomData<&'d mut T>,
|
phantom: PhantomData<&'d mut T>,
|
||||||
phy_type: PhyType,
|
_phy_type: PhyType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'d, T: Instance> UsbOtg<'d, T> {
|
impl<'d, T: Instance> UsbOtg<'d, T> {
|
||||||
@ -44,7 +55,7 @@ impl<'d, T: Instance> UsbOtg<'d, T> {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
phy_type: PhyType::InternalFullSpeed,
|
_phy_type: PhyType::InternalFullSpeed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,7 +82,7 @@ impl<'d, T: Instance> UsbOtg<'d, T> {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
phy_type: PhyType::ExternalHighSpeed,
|
_phy_type: PhyType::ExternalHighSpeed,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,29 +94,6 @@ impl<'d, T: Instance> Drop for UsbOtg<'d, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'d, T: Instance> Send for UsbOtg<'d, T> {}
|
|
||||||
unsafe impl<'d, T: Instance> Sync for UsbOtg<'d, T> {}
|
|
||||||
|
|
||||||
unsafe impl<'d, T: Instance> UsbPeripheral for UsbOtg<'d, T> {
|
|
||||||
const REGISTERS: *const () = T::REGISTERS;
|
|
||||||
const HIGH_SPEED: bool = T::HIGH_SPEED;
|
|
||||||
const FIFO_DEPTH_WORDS: usize = T::FIFO_DEPTH_WORDS;
|
|
||||||
const ENDPOINT_COUNT: usize = T::ENDPOINT_COUNT;
|
|
||||||
|
|
||||||
fn enable() {
|
|
||||||
<T as crate::rcc::sealed::RccPeripheral>::enable();
|
|
||||||
<T as crate::rcc::sealed::RccPeripheral>::reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn phy_type(&self) -> PhyType {
|
|
||||||
self.phy_type
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ahb_frequency_hz(&self) -> u32 {
|
|
||||||
<T as crate::rcc::sealed::RccPeripheral>::frequency().0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) mod sealed {
|
pub(crate) mod sealed {
|
||||||
pub trait Instance {
|
pub trait Instance {
|
||||||
const REGISTERS: *const ();
|
const REGISTERS: *const ();
|
||||||
@ -177,7 +165,7 @@ foreach_peripheral!(
|
|||||||
const FIFO_DEPTH_WORDS: usize = 512;
|
const FIFO_DEPTH_WORDS: usize = 512;
|
||||||
const ENDPOINT_COUNT: usize = 8;
|
const ENDPOINT_COUNT: usize = 8;
|
||||||
} else {
|
} else {
|
||||||
compile_error!("USB_OTG_FS peripheral is not supported by this chip. Disable \"usb-otg-fs\" feature or select a different chip.");
|
compile_error!("USB_OTG_FS peripheral is not supported by this chip.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -214,7 +202,7 @@ foreach_peripheral!(
|
|||||||
const FIFO_DEPTH_WORDS: usize = 1024;
|
const FIFO_DEPTH_WORDS: usize = 1024;
|
||||||
const ENDPOINT_COUNT: usize = 9;
|
const ENDPOINT_COUNT: usize = 9;
|
||||||
} else {
|
} else {
|
||||||
compile_error!("USB_OTG_HS peripheral is not supported by this chip. Disable \"usb-otg-hs\" feature or select a different chip.");
|
compile_error!("USB_OTG_HS peripheral is not supported by this chip.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -222,12 +210,3 @@ foreach_peripheral!(
|
|||||||
impl Instance for peripherals::$inst {}
|
impl Instance for peripherals::$inst {}
|
||||||
};
|
};
|
||||||
);
|
);
|
||||||
|
|
||||||
foreach_interrupt!(
|
|
||||||
($inst:ident, otgfs, $block:ident, GLOBAL, $irq:ident) => {
|
|
||||||
unsafe impl USBInterrupt for crate::interrupt::$irq {}
|
|
||||||
};
|
|
||||||
($inst:ident, otghs, $block:ident, GLOBAL, $irq:ident) => {
|
|
||||||
unsafe impl USBInterrupt for crate::interrupt::$irq {}
|
|
||||||
};
|
|
||||||
);
|
|
||||||
|
@ -8,7 +8,7 @@ resolver = "2"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] }
|
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime", "unstable-traits"] }
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti", "usb-otg"] }
|
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "unstable-traits", "defmt", "stm32f429zi", "unstable-pac", "memory-x", "time-driver-any", "exti"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
defmt-rtt = "0.3"
|
defmt-rtt = "0.3"
|
||||||
|
@ -1,99 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
#![feature(type_alias_impl_trait)]
|
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use panic_probe as _;
|
|
||||||
|
|
||||||
use defmt::{info, unwrap};
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use embassy::interrupt::InterruptExt;
|
|
||||||
use futures::pin_mut;
|
|
||||||
use panic_probe as _; // print out panic messages
|
|
||||||
|
|
||||||
use embassy::executor::Spawner;
|
|
||||||
use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
|
|
||||||
use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial};
|
|
||||||
use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals};
|
|
||||||
use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
|
|
||||||
|
|
||||||
static mut EP_MEMORY: [u32; 2048] = [0; 2048];
|
|
||||||
|
|
||||||
// USB requires at least 48 MHz clock
|
|
||||||
fn config() -> Config {
|
|
||||||
let mut config = Config::default();
|
|
||||||
config.rcc.sys_ck = Some(Hertz(48_000_000));
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy::main(config = "config()")]
|
|
||||||
async fn main(_spawner: Spawner, p: Peripherals) {
|
|
||||||
let mut rx_buffer = [0u8; 64];
|
|
||||||
// we send back input + cr + lf
|
|
||||||
let mut tx_buffer = [0u8; 66];
|
|
||||||
|
|
||||||
let peri = UsbOtg::new_fs(p.USB_OTG_FS, p.PA12, p.PA11);
|
|
||||||
let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY });
|
|
||||||
|
|
||||||
let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
|
|
||||||
|
|
||||||
let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
|
|
||||||
.manufacturer("Fake company")
|
|
||||||
.product("Serial port")
|
|
||||||
.serial_number("TEST")
|
|
||||||
.device_class(0x02)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let irq = interrupt::take!(OTG_FS);
|
|
||||||
irq.set_priority(interrupt::Priority::P3);
|
|
||||||
|
|
||||||
let mut state = State::new();
|
|
||||||
let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
|
|
||||||
pin_mut!(usb);
|
|
||||||
|
|
||||||
let (mut reader, mut writer) = usb.as_ref().take_serial_0();
|
|
||||||
|
|
||||||
info!("usb initialized!");
|
|
||||||
|
|
||||||
unwrap!(
|
|
||||||
writer
|
|
||||||
.write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
|
|
||||||
.await
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut buf = [0u8; 64];
|
|
||||||
loop {
|
|
||||||
let mut n = 0;
|
|
||||||
|
|
||||||
async {
|
|
||||||
loop {
|
|
||||||
let char = unwrap!(reader.read_byte().await);
|
|
||||||
|
|
||||||
if char == b'\r' || char == b'\n' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[n] = char;
|
|
||||||
n += 1;
|
|
||||||
|
|
||||||
// stop if we're out of room
|
|
||||||
if n == buf.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if n > 0 {
|
|
||||||
for char in buf[..n].iter_mut() {
|
|
||||||
// upper case
|
|
||||||
if 0x61 <= *char && *char <= 0x7a {
|
|
||||||
*char &= !0x20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unwrap!(writer.write_all(&buf[..n]).await);
|
|
||||||
unwrap!(writer.write_all(b"\r\n").await);
|
|
||||||
unwrap!(writer.flush().await);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
#![feature(type_alias_impl_trait)]
|
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use panic_probe as _;
|
|
||||||
|
|
||||||
use defmt::{info, unwrap};
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use embassy::interrupt::InterruptExt;
|
|
||||||
use futures::pin_mut;
|
|
||||||
use panic_probe as _; // print out panic messages
|
|
||||||
|
|
||||||
use embassy::executor::Spawner;
|
|
||||||
use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
|
|
||||||
use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial};
|
|
||||||
use embassy_stm32::{interrupt, time::Hertz, Config, Peripherals};
|
|
||||||
use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
|
|
||||||
|
|
||||||
static mut EP_MEMORY: [u32; 2048] = [0; 2048];
|
|
||||||
|
|
||||||
// USB requires at least 48 MHz clock
|
|
||||||
fn config() -> Config {
|
|
||||||
let mut config = Config::default();
|
|
||||||
config.rcc.sys_ck = Some(Hertz(48_000_000));
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy::main(config = "config()")]
|
|
||||||
async fn main(_spawner: Spawner, p: Peripherals) {
|
|
||||||
let mut rx_buffer = [0u8; 64];
|
|
||||||
// we send back input + cr + lf
|
|
||||||
let mut tx_buffer = [0u8; 66];
|
|
||||||
|
|
||||||
// USB with external high-speed PHY
|
|
||||||
let peri = UsbOtg::new_hs_ulpi(
|
|
||||||
p.USB_OTG_HS,
|
|
||||||
p.PA5,
|
|
||||||
p.PC2,
|
|
||||||
p.PC3,
|
|
||||||
p.PC0,
|
|
||||||
p.PA3,
|
|
||||||
p.PB0,
|
|
||||||
p.PB1,
|
|
||||||
p.PB10,
|
|
||||||
p.PB11,
|
|
||||||
p.PB12,
|
|
||||||
p.PB13,
|
|
||||||
p.PB5,
|
|
||||||
);
|
|
||||||
let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY });
|
|
||||||
|
|
||||||
let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
|
|
||||||
|
|
||||||
let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
|
|
||||||
.manufacturer("Fake company")
|
|
||||||
.product("Serial port")
|
|
||||||
.serial_number("TEST")
|
|
||||||
.device_class(0x02)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let irq = interrupt::take!(OTG_FS);
|
|
||||||
irq.set_priority(interrupt::Priority::P3);
|
|
||||||
|
|
||||||
let mut state = State::new();
|
|
||||||
let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
|
|
||||||
pin_mut!(usb);
|
|
||||||
|
|
||||||
let (mut reader, mut writer) = usb.as_ref().take_serial_0();
|
|
||||||
|
|
||||||
info!("usb initialized!");
|
|
||||||
|
|
||||||
unwrap!(
|
|
||||||
writer
|
|
||||||
.write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
|
|
||||||
.await
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut buf = [0u8; 64];
|
|
||||||
loop {
|
|
||||||
let mut n = 0;
|
|
||||||
|
|
||||||
async {
|
|
||||||
loop {
|
|
||||||
let char = unwrap!(reader.read_byte().await);
|
|
||||||
|
|
||||||
if char == b'\r' || char == b'\n' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[n] = char;
|
|
||||||
n += 1;
|
|
||||||
|
|
||||||
// stop if we're out of room
|
|
||||||
if n == buf.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if n > 0 {
|
|
||||||
for char in buf[..n].iter_mut() {
|
|
||||||
// upper case
|
|
||||||
if 0x61 <= *char && *char <= 0x7a {
|
|
||||||
*char &= !0x20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unwrap!(writer.write_all(&buf[..n]).await);
|
|
||||||
unwrap!(writer.write_all(b"\r\n").await);
|
|
||||||
unwrap!(writer.flush().await);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,7 +10,7 @@ resolver = "2"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
|
embassy = { version = "0.1.0", path = "../../embassy", features = ["defmt", "defmt-timestamp-uptime"] }
|
||||||
embassy-traits = { version = "0.1.0", path = "../../embassy-traits" }
|
embassy-traits = { version = "0.1.0", path = "../../embassy-traits" }
|
||||||
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits", "usb-otg"] }
|
embassy-stm32 = { version = "0.1.0", path = "../../embassy-stm32", features = ["nightly", "defmt", "unstable-pac", "stm32l4s5vi", "time-driver-any", "exti", "unstable-traits"] }
|
||||||
|
|
||||||
defmt = "0.3"
|
defmt = "0.3"
|
||||||
defmt-rtt = "0.3"
|
defmt-rtt = "0.3"
|
||||||
|
@ -1,115 +0,0 @@
|
|||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
#![feature(type_alias_impl_trait)]
|
|
||||||
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use panic_probe as _;
|
|
||||||
|
|
||||||
use defmt::{info, unwrap};
|
|
||||||
use defmt_rtt as _; // global logger
|
|
||||||
use embassy::interrupt::InterruptExt;
|
|
||||||
use futures::pin_mut;
|
|
||||||
use panic_probe as _; // print out panic messages
|
|
||||||
|
|
||||||
use embassy::executor::Spawner;
|
|
||||||
use embassy::io::{AsyncBufReadExt, AsyncWriteExt};
|
|
||||||
use embassy_stm32::pac::pwr::vals::Usv;
|
|
||||||
use embassy_stm32::pac::{PWR, RCC};
|
|
||||||
use embassy_stm32::rcc::{ClockSrc, PLLClkDiv, PLLMul, PLLSource, PLLSrcDiv};
|
|
||||||
use embassy_stm32::usb_otg::{State, Usb, UsbBus, UsbOtg, UsbSerial};
|
|
||||||
use embassy_stm32::{interrupt, Config, Peripherals};
|
|
||||||
use usb_device::device::{UsbDeviceBuilder, UsbVidPid};
|
|
||||||
|
|
||||||
static mut EP_MEMORY: [u32; 2048] = [0; 2048];
|
|
||||||
|
|
||||||
// USB requires at least 48 MHz clock
|
|
||||||
fn config() -> Config {
|
|
||||||
let mut config = Config::default();
|
|
||||||
// set up a 80Mhz clock
|
|
||||||
config.rcc.mux = ClockSrc::PLL(
|
|
||||||
PLLSource::HSI16,
|
|
||||||
PLLClkDiv::Div2,
|
|
||||||
PLLSrcDiv::Div2,
|
|
||||||
PLLMul::Mul20,
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
// enable HSI48 clock for USB
|
|
||||||
config.rcc.hsi48 = true;
|
|
||||||
config
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy::main(config = "config()")]
|
|
||||||
async fn main(_spawner: Spawner, p: Peripherals) {
|
|
||||||
// Enable PWR peripheral
|
|
||||||
unsafe { RCC.apb1enr1().modify(|w| w.set_pwren(true)) };
|
|
||||||
unsafe { PWR.cr2().modify(|w| w.set_usv(Usv::VALID)) }
|
|
||||||
|
|
||||||
let mut rx_buffer = [0u8; 64];
|
|
||||||
// we send back input + cr + lf
|
|
||||||
let mut tx_buffer = [0u8; 66];
|
|
||||||
|
|
||||||
let peri = UsbOtg::new_fs(p.USB_OTG_FS, p.PA12, p.PA11);
|
|
||||||
let usb_bus = UsbBus::new(peri, unsafe { &mut EP_MEMORY });
|
|
||||||
|
|
||||||
let serial = UsbSerial::new(&usb_bus, &mut rx_buffer, &mut tx_buffer);
|
|
||||||
|
|
||||||
let device = UsbDeviceBuilder::new(&usb_bus, UsbVidPid(0x16c0, 0x27dd))
|
|
||||||
.manufacturer("Fake company")
|
|
||||||
.product("Serial port")
|
|
||||||
.serial_number("TEST")
|
|
||||||
.device_class(0x02)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let irq = interrupt::take!(OTG_FS);
|
|
||||||
irq.set_priority(interrupt::Priority::P3);
|
|
||||||
|
|
||||||
let mut state = State::new();
|
|
||||||
let usb = unsafe { Usb::new(&mut state, device, serial, irq) };
|
|
||||||
pin_mut!(usb);
|
|
||||||
|
|
||||||
let (mut reader, mut writer) = usb.as_ref().take_serial_0();
|
|
||||||
|
|
||||||
info!("usb initialized!");
|
|
||||||
|
|
||||||
unwrap!(
|
|
||||||
writer
|
|
||||||
.write_all(b"\r\nInput returned upper cased on CR+LF\r\n")
|
|
||||||
.await
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut buf = [0u8; 64];
|
|
||||||
loop {
|
|
||||||
let mut n = 0;
|
|
||||||
|
|
||||||
async {
|
|
||||||
loop {
|
|
||||||
let char = unwrap!(reader.read_byte().await);
|
|
||||||
|
|
||||||
if char == b'\r' || char == b'\n' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[n] = char;
|
|
||||||
n += 1;
|
|
||||||
|
|
||||||
// stop if we're out of room
|
|
||||||
if n == buf.len() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.await;
|
|
||||||
|
|
||||||
if n > 0 {
|
|
||||||
for char in buf[..n].iter_mut() {
|
|
||||||
// upper case
|
|
||||||
if 0x61 <= *char && *char <= 0x7a {
|
|
||||||
*char &= !0x20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unwrap!(writer.write_all(&buf[..n]).await);
|
|
||||||
unwrap!(writer.write_all(b"\r\n").await);
|
|
||||||
unwrap!(writer.flush().await);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user