Eliminated a signal by using a simpler trait method that returns whether VBus power is available. Also includes a UsbSupply that can be signalled for use with the nRF softdevice. Includes the requirement for waiting for power to become available.
119 lines
3.5 KiB
Rust
119 lines
3.5 KiB
Rust
#![no_std]
|
|
#![no_main]
|
|
#![feature(generic_associated_types)]
|
|
#![feature(type_alias_impl_trait)]
|
|
|
|
use core::mem;
|
|
|
|
use defmt::{info, panic, unwrap};
|
|
use embassy::executor::Spawner;
|
|
use embassy::util::Forever;
|
|
use embassy_nrf::usb::{Driver, PowerUsb};
|
|
use embassy_nrf::{interrupt, pac, peripherals, Peripherals};
|
|
use embassy_usb::driver::EndpointError;
|
|
use embassy_usb::{Builder, Config, UsbDevice};
|
|
use embassy_usb_serial::{CdcAcmClass, State};
|
|
use {defmt_rtt as _, panic_probe as _};
|
|
|
|
type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>;
|
|
|
|
#[embassy::task]
|
|
async fn usb_task(mut device: UsbDevice<'static, MyDriver>) {
|
|
device.run().await;
|
|
}
|
|
|
|
#[embassy::task]
|
|
async fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) {
|
|
loop {
|
|
class.wait_connection().await;
|
|
info!("Connected");
|
|
let _ = echo(&mut class).await;
|
|
info!("Disconnected");
|
|
}
|
|
}
|
|
|
|
#[embassy::main]
|
|
async fn main(spawner: Spawner, p: Peripherals) {
|
|
let clock: pac::CLOCK = unsafe { mem::transmute(()) };
|
|
|
|
info!("Enabling ext hfosc...");
|
|
clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
|
|
while clock.events_hfclkstarted.read().bits() != 1 {}
|
|
// Create the driver, from the HAL.
|
|
let irq = interrupt::take!(USBD);
|
|
let power_irq = interrupt::take!(POWER_CLOCK);
|
|
let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
|
|
|
|
// Create embassy-usb Config
|
|
let mut config = Config::new(0xc0de, 0xcafe);
|
|
config.manufacturer = Some("Embassy");
|
|
config.product = Some("USB-serial example");
|
|
config.serial_number = Some("12345678");
|
|
config.max_power = 100;
|
|
config.max_packet_size_0 = 64;
|
|
|
|
// Required for windows compatiblity.
|
|
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
|
|
config.device_class = 0xEF;
|
|
config.device_sub_class = 0x02;
|
|
config.device_protocol = 0x01;
|
|
config.composite_with_iads = true;
|
|
|
|
struct Resources {
|
|
device_descriptor: [u8; 256],
|
|
config_descriptor: [u8; 256],
|
|
bos_descriptor: [u8; 256],
|
|
control_buf: [u8; 64],
|
|
serial_state: State<'static>,
|
|
}
|
|
static RESOURCES: Forever<Resources> = Forever::new();
|
|
let res = RESOURCES.put(Resources {
|
|
device_descriptor: [0; 256],
|
|
config_descriptor: [0; 256],
|
|
bos_descriptor: [0; 256],
|
|
control_buf: [0; 64],
|
|
serial_state: State::new(),
|
|
});
|
|
|
|
// Create embassy-usb DeviceBuilder using the driver and config.
|
|
let mut builder = Builder::new(
|
|
driver,
|
|
config,
|
|
&mut res.device_descriptor,
|
|
&mut res.config_descriptor,
|
|
&mut res.bos_descriptor,
|
|
&mut res.control_buf,
|
|
None,
|
|
);
|
|
|
|
// Create classes on the builder.
|
|
let class = CdcAcmClass::new(&mut builder, &mut res.serial_state, 64);
|
|
|
|
// Build the builder.
|
|
let usb = builder.build();
|
|
|
|
unwrap!(spawner.spawn(usb_task(usb)));
|
|
unwrap!(spawner.spawn(echo_task(class)));
|
|
}
|
|
|
|
struct Disconnected {}
|
|
|
|
impl From<EndpointError> for Disconnected {
|
|
fn from(val: EndpointError) -> Self {
|
|
match val {
|
|
EndpointError::BufferOverflow => panic!("Buffer overflow"),
|
|
EndpointError::Disabled => Disconnected {},
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn echo(class: &mut CdcAcmClass<'static, MyDriver>) -> Result<(), Disconnected> {
|
|
let mut buf = [0; 64];
|
|
loop {
|
|
let n = class.read_packet(&mut buf).await?;
|
|
let data = &buf[..n];
|
|
info!("data: {:x}", data);
|
|
class.write_packet(data).await?;
|
|
}
|
|
}
|