810: Takes care of power for nRF USB devices r=Dirbaio a=huntc

Modifies the usb-serial example to illustrate how to setup USB for situations where the USB power can be detected and removed.

Gaps:

~~* No support for the nrf-softdevices as yet, although this should be possible via another constructor.~~
* No support for the nrf5340, although this should be possible via USBREG.

The change is tested and appears to work. Some notes:

* There's an existing field named self_powered as a UsbDevice field. It doesn't ever appear to get set. I'm wondering if this field is intended to signal that a device has the nRF VBUS power situation or not. I'm not presently using it.
* The new PowerDetected event is generated on the bus initially in situations where just new is used i.e. without power management, including on STM. We can therefore rely on this event always being generated.

Old description:

~~EnabledUsbDevice is a wrapper around the `UsbDevice` where its enablement is also subject to external events, such as `POWER` events for nRF. It is introduced generically to support other platforms should they also require external signaling for enablement.~~

Co-authored-by: huntc <huntchr@gmail.com>
This commit is contained in:
bors[bot] 2022-07-11 00:01:41 +00:00 committed by GitHub
commit 9753f76794
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 275 additions and 159 deletions

View File

@ -2,7 +2,7 @@
use core::marker::PhantomData; use core::marker::PhantomData;
use core::mem::MaybeUninit; use core::mem::MaybeUninit;
use core::sync::atomic::{compiler_fence, AtomicU32, Ordering}; use core::sync::atomic::{compiler_fence, AtomicBool, AtomicU32, Ordering};
use core::task::Poll; use core::task::Poll;
use cortex_m::peripheral::NVIC; use cortex_m::peripheral::NVIC;
@ -26,14 +26,149 @@ static EP_IN_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8]; static EP_OUT_WAKERS: [AtomicWaker; 8] = [NEW_AW; 8];
static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0); static READY_ENDPOINTS: AtomicU32 = AtomicU32::new(0);
pub struct Driver<'d, T: Instance> { /// There are multiple ways to detect USB power. The behavior
/// here provides a hook into determining whether it is.
pub trait UsbSupply {
fn is_usb_detected(&self) -> bool;
type UsbPowerReadyFuture<'a>: Future<Output = Result<(), ()>> + 'a
where
Self: 'a;
fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_>;
}
pub struct Driver<'d, T: Instance, P: UsbSupply> {
phantom: PhantomData<&'d mut T>, phantom: PhantomData<&'d mut T>,
alloc_in: Allocator, alloc_in: Allocator,
alloc_out: Allocator, alloc_out: Allocator,
usb_supply: P,
} }
impl<'d, T: Instance> Driver<'d, T> { /// Uses the POWER peripheral to detect when power is available
pub fn new(_usb: impl Unborrow<Target = T> + 'd, irq: impl Unborrow<Target = T::Interrupt> + 'd) -> Self { /// for USB. Unsuitable for usage with the nRF softdevice.
#[cfg(not(feature = "_nrf5340-app"))]
pub struct PowerUsb {}
/// Can be used to signal that power is available. Particularly suited for
/// use with the nRF softdevice.
pub struct SignalledSupply {
usb_detected: AtomicBool,
power_ready: AtomicBool,
}
static POWER_WAKER: AtomicWaker = NEW_AW;
#[cfg(not(feature = "_nrf5340-app"))]
impl PowerUsb {
pub fn new(power_irq: impl Interrupt) -> Self {
let regs = unsafe { &*pac::POWER::ptr() };
power_irq.set_handler(Self::on_interrupt);
power_irq.unpend();
power_irq.enable();
regs.intenset
.write(|w| w.usbdetected().set().usbremoved().set().usbpwrrdy().set());
Self {}
}
#[cfg(not(feature = "_nrf5340-app"))]
fn on_interrupt(_: *mut ()) {
let regs = unsafe { &*pac::POWER::ptr() };
if regs.events_usbdetected.read().bits() != 0 {
regs.events_usbdetected.reset();
BUS_WAKER.wake();
}
if regs.events_usbremoved.read().bits() != 0 {
regs.events_usbremoved.reset();
BUS_WAKER.wake();
POWER_WAKER.wake();
}
if regs.events_usbpwrrdy.read().bits() != 0 {
regs.events_usbpwrrdy.reset();
POWER_WAKER.wake();
}
}
}
#[cfg(not(feature = "_nrf5340-app"))]
impl UsbSupply for PowerUsb {
fn is_usb_detected(&self) -> bool {
let regs = unsafe { &*pac::POWER::ptr() };
regs.usbregstatus.read().vbusdetect().is_vbus_present()
}
type UsbPowerReadyFuture<'a> = impl Future<Output = Result<(), ()>> + 'a where Self: 'a;
fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_> {
poll_fn(move |cx| {
POWER_WAKER.register(cx.waker());
let regs = unsafe { &*pac::POWER::ptr() };
if regs.usbregstatus.read().outputrdy().is_ready() {
Poll::Ready(Ok(()))
} else if !self.is_usb_detected() {
Poll::Ready(Err(()))
} else {
Poll::Pending
}
})
}
}
impl SignalledSupply {
pub fn new(usb_detected: bool, power_ready: bool) -> Self {
BUS_WAKER.wake();
Self {
usb_detected: AtomicBool::new(usb_detected),
power_ready: AtomicBool::new(power_ready),
}
}
pub fn detected(&self, detected: bool) {
self.usb_detected.store(detected, Ordering::Relaxed);
self.power_ready.store(false, Ordering::Relaxed);
BUS_WAKER.wake();
POWER_WAKER.wake();
}
pub fn ready(&self) {
self.power_ready.store(true, Ordering::Relaxed);
POWER_WAKER.wake();
}
}
impl UsbSupply for SignalledSupply {
fn is_usb_detected(&self) -> bool {
self.usb_detected.load(Ordering::Relaxed)
}
type UsbPowerReadyFuture<'a> = impl Future<Output = Result<(), ()>> + 'a where Self: 'a;
fn wait_power_ready(&mut self) -> Self::UsbPowerReadyFuture<'_> {
poll_fn(move |cx| {
POWER_WAKER.register(cx.waker());
if self.power_ready.load(Ordering::Relaxed) {
Poll::Ready(Ok(()))
} else if !self.usb_detected.load(Ordering::Relaxed) {
Poll::Ready(Err(()))
} else {
Poll::Pending
}
})
}
}
impl<'d, T: Instance, P: UsbSupply> Driver<'d, T, P> {
pub fn new(
_usb: impl Unborrow<Target = T> + 'd,
irq: impl Unborrow<Target = T::Interrupt> + 'd,
usb_supply: P,
) -> Self {
unborrow!(irq); unborrow!(irq);
irq.set_handler(Self::on_interrupt); irq.set_handler(Self::on_interrupt);
irq.unpend(); irq.unpend();
@ -43,6 +178,7 @@ impl<'d, T: Instance> Driver<'d, T> {
phantom: PhantomData, phantom: PhantomData,
alloc_in: Allocator::new(), alloc_in: Allocator::new(),
alloc_out: Allocator::new(), alloc_out: Allocator::new(),
usb_supply,
} }
} }
@ -76,7 +212,6 @@ impl<'d, T: Instance> Driver<'d, T> {
// doesn't cause an infinite irq loop. // doesn't cause an infinite irq loop.
if regs.events_usbevent.read().bits() != 0 { if regs.events_usbevent.read().bits() != 0 {
regs.events_usbevent.reset(); regs.events_usbevent.reset();
//regs.intenclr.write(|w| w.usbevent().clear());
BUS_WAKER.wake(); BUS_WAKER.wake();
} }
@ -98,11 +233,11 @@ impl<'d, T: Instance> Driver<'d, T> {
} }
} }
impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> { impl<'d, T: Instance, P: UsbSupply + 'd> driver::Driver<'d> for Driver<'d, T, P> {
type EndpointOut = Endpoint<'d, T, Out>; type EndpointOut = Endpoint<'d, T, Out>;
type EndpointIn = Endpoint<'d, T, In>; type EndpointIn = Endpoint<'d, T, In>;
type ControlPipe = ControlPipe<'d, T>; type ControlPipe = ControlPipe<'d, T>;
type Bus = Bus<'d, T>; type Bus = Bus<'d, T, P>;
fn alloc_endpoint_in( fn alloc_endpoint_in(
&mut self, &mut self,
@ -138,7 +273,11 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) { fn start(self, control_max_packet_size: u16) -> (Self::Bus, Self::ControlPipe) {
( (
Bus { phantom: PhantomData }, Bus {
phantom: PhantomData,
power_available: false,
usb_supply: self.usb_supply,
},
ControlPipe { ControlPipe {
_phantom: PhantomData, _phantom: PhantomData,
max_packet_size: control_max_packet_size, max_packet_size: control_max_packet_size,
@ -147,11 +286,13 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
} }
} }
pub struct Bus<'d, T: Instance> { pub struct Bus<'d, T: Instance, P: UsbSupply> {
phantom: PhantomData<&'d mut T>, phantom: PhantomData<&'d mut T>,
power_available: bool,
usb_supply: P,
} }
impl<'d, T: Instance> driver::Bus for Bus<'d, T> { impl<'d, T: Instance, P: UsbSupply> driver::Bus for Bus<'d, T, P> {
type EnableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a; type EnableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
type DisableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a; type DisableFuture<'a> = impl Future<Output = ()> + 'a where Self: 'a;
type PollFuture<'a> = impl Future<Output = Event> + 'a where Self: 'a; type PollFuture<'a> = impl Future<Output = Event> + 'a where Self: 'a;
@ -188,9 +329,14 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
w.epdata().set_bit(); w.epdata().set_bit();
w w
}); });
// Enable the USB pullup, allowing enumeration.
regs.usbpullup.write(|w| w.connect().enabled()); if self.usb_supply.wait_power_ready().await.is_ok() {
trace!("enabled"); // Enable the USB pullup, allowing enumeration.
regs.usbpullup.write(|w| w.connect().enabled());
trace!("enabled");
} else {
trace!("usb power not ready due to usb removal");
}
} }
} }
@ -246,6 +392,17 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
trace!("USB event: ready"); trace!("USB event: ready");
} }
if self.usb_supply.is_usb_detected() != self.power_available {
self.power_available = !self.power_available;
if self.power_available {
trace!("Power event: available");
return Poll::Ready(Event::PowerDetected);
} else {
trace!("Power event: removed");
return Poll::Ready(Event::PowerRemoved);
}
}
Poll::Pending Poll::Pending
}) })
} }

View File

@ -161,6 +161,9 @@ impl<'d, T: Instance> Driver<'d, T> {
dm.set_as_af(dm.af_num(), AFType::OutputPushPull); dm.set_as_af(dm.af_num(), AFType::OutputPushPull);
} }
// Initialize the bus so that it signals that power is available
BUS_WAKER.wake();
Self { Self {
phantom: PhantomData, phantom: PhantomData,
alloc: [EndpointData { alloc: [EndpointData {
@ -406,6 +409,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
Bus { Bus {
phantom: PhantomData, phantom: PhantomData,
ep_types, ep_types,
inited: false,
}, },
ControlPipe { ControlPipe {
_phantom: PhantomData, _phantom: PhantomData,
@ -420,6 +424,7 @@ impl<'d, T: Instance> driver::Driver<'d> for Driver<'d, T> {
pub struct Bus<'d, T: Instance> { pub struct Bus<'d, T: Instance> {
phantom: PhantomData<&'d mut T>, phantom: PhantomData<&'d mut T>,
ep_types: [EpType; EP_COUNT - 1], ep_types: [EpType; EP_COUNT - 1],
inited: bool,
} }
impl<'d, T: Instance> driver::Bus for Bus<'d, T> { impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
@ -428,53 +433,59 @@ impl<'d, T: Instance> driver::Bus for Bus<'d, T> {
fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> { fn poll<'a>(&'a mut self) -> Self::PollFuture<'a> {
poll_fn(move |cx| unsafe { poll_fn(move |cx| unsafe {
BUS_WAKER.register(cx.waker()); BUS_WAKER.register(cx.waker());
let regs = T::regs();
let flags = IRQ_FLAGS.load(Ordering::Acquire); if self.inited {
let regs = T::regs();
if flags & IRQ_FLAG_RESUME != 0 { let flags = IRQ_FLAGS.load(Ordering::Acquire);
IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel);
return Poll::Ready(Event::Resume);
}
if flags & IRQ_FLAG_RESET != 0 { if flags & IRQ_FLAG_RESUME != 0 {
IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel); IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESUME, Ordering::AcqRel);
return Poll::Ready(Event::Resume);
trace!("RESET REGS WRITINGINGING");
regs.daddr().write(|w| {
w.set_ef(true);
w.set_add(0);
});
regs.epr(0).write(|w| {
w.set_ep_type(EpType::CONTROL);
w.set_stat_rx(Stat::NAK);
w.set_stat_tx(Stat::NAK);
});
for i in 1..EP_COUNT {
regs.epr(i).write(|w| {
w.set_ea(i as _);
w.set_ep_type(self.ep_types[i - 1]);
})
} }
for w in &EP_IN_WAKERS { if flags & IRQ_FLAG_RESET != 0 {
w.wake() IRQ_FLAGS.fetch_and(!IRQ_FLAG_RESET, Ordering::AcqRel);
}
for w in &EP_OUT_WAKERS { trace!("RESET REGS WRITINGINGING");
w.wake() regs.daddr().write(|w| {
w.set_ef(true);
w.set_add(0);
});
regs.epr(0).write(|w| {
w.set_ep_type(EpType::CONTROL);
w.set_stat_rx(Stat::NAK);
w.set_stat_tx(Stat::NAK);
});
for i in 1..EP_COUNT {
regs.epr(i).write(|w| {
w.set_ea(i as _);
w.set_ep_type(self.ep_types[i - 1]);
})
}
for w in &EP_IN_WAKERS {
w.wake()
}
for w in &EP_OUT_WAKERS {
w.wake()
}
return Poll::Ready(Event::Reset);
} }
return Poll::Ready(Event::Reset); if flags & IRQ_FLAG_SUSPEND != 0 {
} IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel);
return Poll::Ready(Event::Suspend);
}
if flags & IRQ_FLAG_SUSPEND != 0 { Poll::Pending
IRQ_FLAGS.fetch_and(!IRQ_FLAG_SUSPEND, Ordering::AcqRel); } else {
return Poll::Ready(Event::Suspend); self.inited = true;
return Poll::Ready(Event::PowerDetected);
} }
Poll::Pending
}) })
} }

View File

@ -202,6 +202,12 @@ pub enum Event {
/// A USB resume request has been detected after being suspended or, in the case of self-powered /// A USB resume request has been detected after being suspended or, in the case of self-powered
/// devices, the device has been connected to the USB bus. /// devices, the device has been connected to the USB bus.
Resume, Resume,
/// The USB power has been detected.
PowerDetected,
/// The USB power has been removed. Not supported by all devices.
PowerRemoved,
} }
#[derive(Copy, Clone, Eq, PartialEq, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Debug)]

View File

@ -30,6 +30,9 @@ use crate::driver::ControlPipe;
#[derive(PartialEq, Eq, Copy, Clone, Debug)] #[derive(PartialEq, Eq, Copy, Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum UsbDeviceState { pub enum UsbDeviceState {
/// The USB device has no power.
Unpowered,
/// The USB device is disabled. /// The USB device is disabled.
Disabled, Disabled,
@ -154,7 +157,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
config_descriptor, config_descriptor,
bos_descriptor, bos_descriptor,
device_state: UsbDeviceState::Disabled, device_state: UsbDeviceState::Unpowered,
suspended: false, suspended: false,
remote_wakeup_enabled: false, remote_wakeup_enabled: false,
self_powered: false, self_powered: false,
@ -185,20 +188,11 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
/// before calling any other `UsbDevice` methods to fully reset the /// before calling any other `UsbDevice` methods to fully reset the
/// peripheral. /// peripheral.
pub async fn run_until_suspend(&mut self) -> () { pub async fn run_until_suspend(&mut self) -> () {
if self.inner.device_state == UsbDeviceState::Disabled {
self.inner.bus.enable().await;
self.inner.device_state = UsbDeviceState::Default;
if let Some(h) = &self.inner.handler {
h.enabled(true);
}
}
while !self.inner.suspended { while !self.inner.suspended {
let control_fut = self.control.setup(); let control_fut = self.control.setup();
let bus_fut = self.inner.bus.poll(); let bus_fut = self.inner.bus.poll();
match select(bus_fut, control_fut).await { match select(bus_fut, control_fut).await {
Either::First(evt) => self.inner.handle_bus_event(evt), Either::First(evt) => self.inner.handle_bus_event(evt).await,
Either::Second(req) => self.handle_control(req).await, Either::Second(req) => self.handle_control(req).await,
} }
} }
@ -224,7 +218,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
pub async fn wait_resume(&mut self) { pub async fn wait_resume(&mut self) {
while self.inner.suspended { while self.inner.suspended {
let evt = self.inner.bus.poll().await; let evt = self.inner.bus.poll().await;
self.inner.handle_bus_event(evt); self.inner.handle_bus_event(evt).await;
} }
} }
@ -341,7 +335,7 @@ impl<'d, D: Driver<'d>> UsbDevice<'d, D> {
} }
impl<'d, D: Driver<'d>> Inner<'d, D> { impl<'d, D: Driver<'d>> Inner<'d, D> {
fn handle_bus_event(&mut self, evt: Event) { async fn handle_bus_event(&mut self, evt: Event) {
match evt { match evt {
Event::Reset => { Event::Reset => {
trace!("usb: reset"); trace!("usb: reset");
@ -376,6 +370,24 @@ impl<'d, D: Driver<'d>> Inner<'d, D> {
h.suspended(true); h.suspended(true);
} }
} }
Event::PowerDetected => {
trace!("usb: power detected");
self.bus.enable().await;
self.device_state = UsbDeviceState::Default;
if let Some(h) = &self.handler {
h.enabled(true);
}
}
Event::PowerRemoved => {
trace!("usb: power removed");
self.bus.disable().await;
self.device_state = UsbDeviceState::Unpowered;
if let Some(h) = &self.handler {
h.enabled(false);
}
}
} }
} }

View File

@ -15,14 +15,14 @@ use embassy::util::Forever;
use embassy_net::tcp::TcpSocket; use embassy_net::tcp::TcpSocket;
use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources}; use embassy_net::{PacketBox, PacketBoxExt, PacketBuf, Stack, StackResources};
use embassy_nrf::rng::Rng; use embassy_nrf::rng::Rng;
use embassy_nrf::usb::Driver; use embassy_nrf::usb::{Driver, PowerUsb};
use embassy_nrf::{interrupt, pac, peripherals, Peripherals}; use embassy_nrf::{interrupt, pac, peripherals, Peripherals};
use embassy_usb::{Builder, Config, UsbDevice}; use embassy_usb::{Builder, Config, UsbDevice};
use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State}; use embassy_usb_ncm::{CdcNcmClass, Receiver, Sender, State};
use embedded_io::asynch::{Read, Write}; use embedded_io::asynch::{Read, Write};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
type MyDriver = Driver<'static, peripherals::USBD>; type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>;
macro_rules! forever { macro_rules! forever {
($val:expr) => {{ ($val:expr) => {{
@ -84,19 +84,15 @@ async fn net_task(stack: &'static Stack<Device>) -> ! {
#[embassy::main] #[embassy::main]
async fn main(spawner: Spawner, p: Peripherals) { async fn main(spawner: Spawner, p: Peripherals) {
let clock: pac::CLOCK = unsafe { mem::transmute(()) }; let clock: pac::CLOCK = unsafe { mem::transmute(()) };
let power: pac::POWER = unsafe { mem::transmute(()) };
info!("Enabling ext hfosc..."); info!("Enabling ext hfosc...");
clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
while clock.events_hfclkstarted.read().bits() != 1 {} while clock.events_hfclkstarted.read().bits() != 1 {}
info!("Waiting for vbus...");
while !power.usbregstatus.read().vbusdetect().is_vbus_present() {}
info!("vbus OK");
// Create the driver, from the HAL. // Create the driver, from the HAL.
let irq = interrupt::take!(USBD); let irq = interrupt::take!(USBD);
let driver = Driver::new(p.USBD, irq); let power_irq = interrupt::take!(POWER_CLOCK);
let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
// Create embassy-usb Config // Create embassy-usb Config
let mut config = Config::new(0xc0de, 0xcafe); let mut config = Config::new(0xc0de, 0xcafe);

View File

@ -10,10 +10,9 @@ use defmt::*;
use embassy::channel::signal::Signal; use embassy::channel::signal::Signal;
use embassy::executor::Spawner; use embassy::executor::Spawner;
use embassy::time::Duration; use embassy::time::Duration;
use embassy::util::{select, select3, Either, Either3}; use embassy::util::{select, Either};
use embassy_nrf::gpio::{Input, Pin, Pull}; use embassy_nrf::gpio::{Input, Pin, Pull};
use embassy_nrf::interrupt::InterruptExt; use embassy_nrf::usb::{Driver, PowerUsb};
use embassy_nrf::usb::Driver;
use embassy_nrf::{interrupt, pac, Peripherals}; use embassy_nrf::{interrupt, pac, Peripherals};
use embassy_usb::control::OutResponse; use embassy_usb::control::OutResponse;
use embassy_usb::{Builder, Config, DeviceStateHandler}; use embassy_usb::{Builder, Config, DeviceStateHandler};
@ -22,29 +21,11 @@ use futures::future::join;
use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
static ENABLE_USB: Signal<bool> = Signal::new();
static SUSPENDED: AtomicBool = AtomicBool::new(false); static SUSPENDED: AtomicBool = AtomicBool::new(false);
fn on_power_interrupt(_: *mut ()) {
let regs = unsafe { &*pac::POWER::ptr() };
if regs.events_usbdetected.read().bits() != 0 {
regs.events_usbdetected.reset();
info!("Vbus detected, enabling USB...");
ENABLE_USB.signal(true);
}
if regs.events_usbremoved.read().bits() != 0 {
regs.events_usbremoved.reset();
info!("Vbus removed, disabling USB...");
ENABLE_USB.signal(false);
}
}
#[embassy::main] #[embassy::main]
async fn main(_spawner: Spawner, p: Peripherals) { async fn main(_spawner: Spawner, p: Peripherals) {
let clock: pac::CLOCK = unsafe { mem::transmute(()) }; let clock: pac::CLOCK = unsafe { mem::transmute(()) };
let power: pac::POWER = unsafe { mem::transmute(()) };
info!("Enabling ext hfosc..."); info!("Enabling ext hfosc...");
clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
@ -52,7 +33,8 @@ async fn main(_spawner: Spawner, p: Peripherals) {
// Create the driver, from the HAL. // Create the driver, from the HAL.
let irq = interrupt::take!(USBD); let irq = interrupt::take!(USBD);
let driver = Driver::new(p.USBD, irq); let power_irq = interrupt::take!(POWER_CLOCK);
let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
// Create embassy-usb Config // Create embassy-usb Config
let mut config = Config::new(0xc0de, 0xcafe); let mut config = Config::new(0xc0de, 0xcafe);
@ -100,31 +82,11 @@ async fn main(_spawner: Spawner, p: Peripherals) {
// Run the USB device. // Run the USB device.
let usb_fut = async { let usb_fut = async {
enable_command().await;
loop { loop {
match select(usb.run_until_suspend(), ENABLE_USB.wait()).await { usb.run_until_suspend().await;
Either::First(_) => {} match select(usb.wait_resume(), remote_wakeup.wait()).await {
Either::Second(enable) => { Either::First(_) => (),
if enable { Either::Second(_) => unwrap!(usb.remote_wakeup().await),
warn!("Enable when already enabled!");
} else {
usb.disable().await;
enable_command().await;
}
}
}
match select3(usb.wait_resume(), ENABLE_USB.wait(), remote_wakeup.wait()).await {
Either3::First(_) => (),
Either3::Second(enable) => {
if enable {
warn!("Enable when already enabled!");
} else {
usb.disable().await;
enable_command().await;
}
}
Either3::Third(_) => unwrap!(usb.remote_wakeup().await),
} }
} }
}; };
@ -174,28 +136,11 @@ async fn main(_spawner: Spawner, p: Peripherals) {
reader.run(false, &request_handler).await; reader.run(false, &request_handler).await;
}; };
let power_irq = interrupt::take!(POWER_CLOCK);
power_irq.set_handler(on_power_interrupt);
power_irq.unpend();
power_irq.enable();
power.intenset.write(|w| w.usbdetected().set().usbremoved().set());
// Run everything concurrently. // Run everything concurrently.
// If we had made everything `'static` above instead, we could do this using separate tasks instead. // If we had made everything `'static` above instead, we could do this using separate tasks instead.
join(usb_fut, join(in_fut, out_fut)).await; join(usb_fut, join(in_fut, out_fut)).await;
} }
async fn enable_command() {
loop {
if ENABLE_USB.wait().await {
break;
} else {
warn!("Received disable signal when already disabled!");
}
}
}
struct MyRequestHandler {} struct MyRequestHandler {}
impl RequestHandler for MyRequestHandler { impl RequestHandler for MyRequestHandler {

View File

@ -8,7 +8,7 @@ use core::mem;
use defmt::*; use defmt::*;
use embassy::executor::Spawner; use embassy::executor::Spawner;
use embassy::time::{Duration, Timer}; use embassy::time::{Duration, Timer};
use embassy_nrf::usb::Driver; use embassy_nrf::usb::{Driver, PowerUsb};
use embassy_nrf::{interrupt, pac, Peripherals}; use embassy_nrf::{interrupt, pac, Peripherals};
use embassy_usb::control::OutResponse; use embassy_usb::control::OutResponse;
use embassy_usb::{Builder, Config}; use embassy_usb::{Builder, Config};
@ -20,19 +20,15 @@ use {defmt_rtt as _, panic_probe as _};
#[embassy::main] #[embassy::main]
async fn main(_spawner: Spawner, p: Peripherals) { async fn main(_spawner: Spawner, p: Peripherals) {
let clock: pac::CLOCK = unsafe { mem::transmute(()) }; let clock: pac::CLOCK = unsafe { mem::transmute(()) };
let power: pac::POWER = unsafe { mem::transmute(()) };
info!("Enabling ext hfosc..."); info!("Enabling ext hfosc...");
clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
while clock.events_hfclkstarted.read().bits() != 1 {} while clock.events_hfclkstarted.read().bits() != 1 {}
info!("Waiting for vbus...");
while !power.usbregstatus.read().vbusdetect().is_vbus_present() {}
info!("vbus OK");
// Create the driver, from the HAL. // Create the driver, from the HAL.
let irq = interrupt::take!(USBD); let irq = interrupt::take!(USBD);
let driver = Driver::new(p.USBD, irq); let power_irq = interrupt::take!(POWER_CLOCK);
let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
// Create embassy-usb Config // Create embassy-usb Config
let mut config = Config::new(0xc0de, 0xcafe); let mut config = Config::new(0xc0de, 0xcafe);

View File

@ -7,7 +7,7 @@ use core::mem;
use defmt::{info, panic}; use defmt::{info, panic};
use embassy::executor::Spawner; use embassy::executor::Spawner;
use embassy_nrf::usb::{Driver, Instance}; use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply};
use embassy_nrf::{interrupt, pac, Peripherals}; use embassy_nrf::{interrupt, pac, Peripherals};
use embassy_usb::driver::EndpointError; use embassy_usb::driver::EndpointError;
use embassy_usb::{Builder, Config}; use embassy_usb::{Builder, Config};
@ -18,19 +18,15 @@ use {defmt_rtt as _, panic_probe as _};
#[embassy::main] #[embassy::main]
async fn main(_spawner: Spawner, p: Peripherals) { async fn main(_spawner: Spawner, p: Peripherals) {
let clock: pac::CLOCK = unsafe { mem::transmute(()) }; let clock: pac::CLOCK = unsafe { mem::transmute(()) };
let power: pac::POWER = unsafe { mem::transmute(()) };
info!("Enabling ext hfosc..."); info!("Enabling ext hfosc...");
clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
while clock.events_hfclkstarted.read().bits() != 1 {} while clock.events_hfclkstarted.read().bits() != 1 {}
info!("Waiting for vbus...");
while !power.usbregstatus.read().vbusdetect().is_vbus_present() {}
info!("vbus OK");
// Create the driver, from the HAL. // Create the driver, from the HAL.
let irq = interrupt::take!(USBD); let irq = interrupt::take!(USBD);
let driver = Driver::new(p.USBD, irq); let power_irq = interrupt::take!(POWER_CLOCK);
let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
// Create embassy-usb Config // Create embassy-usb Config
let mut config = Config::new(0xc0de, 0xcafe); let mut config = Config::new(0xc0de, 0xcafe);
@ -101,7 +97,9 @@ impl From<EndpointError> for Disconnected {
} }
} }
async fn echo<'d, T: Instance + 'd>(class: &mut CdcAcmClass<'d, Driver<'d, T>>) -> Result<(), Disconnected> { async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>(
class: &mut CdcAcmClass<'d, Driver<'d, T, P>>,
) -> Result<(), Disconnected> {
let mut buf = [0; 64]; let mut buf = [0; 64];
loop { loop {
let n = class.read_packet(&mut buf).await?; let n = class.read_packet(&mut buf).await?;

View File

@ -8,14 +8,14 @@ use core::mem;
use defmt::{info, panic, unwrap}; use defmt::{info, panic, unwrap};
use embassy::executor::Spawner; use embassy::executor::Spawner;
use embassy::util::Forever; use embassy::util::Forever;
use embassy_nrf::usb::Driver; use embassy_nrf::usb::{Driver, PowerUsb};
use embassy_nrf::{interrupt, pac, peripherals, Peripherals}; use embassy_nrf::{interrupt, pac, peripherals, Peripherals};
use embassy_usb::driver::EndpointError; use embassy_usb::driver::EndpointError;
use embassy_usb::{Builder, Config, UsbDevice}; use embassy_usb::{Builder, Config, UsbDevice};
use embassy_usb_serial::{CdcAcmClass, State}; use embassy_usb_serial::{CdcAcmClass, State};
use {defmt_rtt as _, panic_probe as _}; use {defmt_rtt as _, panic_probe as _};
type MyDriver = Driver<'static, peripherals::USBD>; type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>;
#[embassy::task] #[embassy::task]
async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { async fn usb_task(mut device: UsbDevice<'static, MyDriver>) {
@ -35,19 +35,14 @@ async fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) {
#[embassy::main] #[embassy::main]
async fn main(spawner: Spawner, p: Peripherals) { async fn main(spawner: Spawner, p: Peripherals) {
let clock: pac::CLOCK = unsafe { mem::transmute(()) }; let clock: pac::CLOCK = unsafe { mem::transmute(()) };
let power: pac::POWER = unsafe { mem::transmute(()) };
info!("Enabling ext hfosc..."); info!("Enabling ext hfosc...");
clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) });
while clock.events_hfclkstarted.read().bits() != 1 {} while clock.events_hfclkstarted.read().bits() != 1 {}
info!("Waiting for vbus...");
while !power.usbregstatus.read().vbusdetect().is_vbus_present() {}
info!("vbus OK");
// Create the driver, from the HAL. // Create the driver, from the HAL.
let irq = interrupt::take!(USBD); let irq = interrupt::take!(USBD);
let driver = Driver::new(p.USBD, irq); let power_irq = interrupt::take!(POWER_CLOCK);
let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq));
// Create embassy-usb Config // Create embassy-usb Config
let mut config = Config::new(0xc0de, 0xcafe); let mut config = Config::new(0xc0de, 0xcafe);