mirror of
https://github.com/ferrous-systems/embedded-trainings-2020.git
synced 2024-06-01 19:41:38 +00:00
Cleaned up lib
This commit is contained in:
parent
3f56cd66d8
commit
a9293e68a6
|
@ -1,13 +0,0 @@
|
||||||
/// USBD cannot be enabled
|
|
||||||
pub unsafe fn e187a() {
|
|
||||||
(0x4006_EC00 as *mut u32).write_volatile(0x9375);
|
|
||||||
(0x4006_ED14 as *mut u32).write_volatile(3);
|
|
||||||
(0x4006_EC00 as *mut u32).write_volatile(0x9375);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// USBD cannot be enabled
|
|
||||||
pub unsafe fn e187b() {
|
|
||||||
(0x4006_EC00 as *mut u32).write_volatile(0x9375);
|
|
||||||
(0x4006_ED14 as *mut u32).write_volatile(0);
|
|
||||||
(0x4006_EC00 as *mut u32).write_volatile(0x9375);
|
|
||||||
}
|
|
|
@ -7,38 +7,23 @@
|
||||||
use core::{
|
use core::{
|
||||||
ops,
|
ops,
|
||||||
fmt,
|
fmt,
|
||||||
sync::atomic::{self, AtomicU32, Ordering},
|
sync::atomic::{self, Ordering},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use cortex_m::{asm, peripheral::NVIC};
|
use cortex_m::asm;
|
||||||
use embedded_hal::digital::v2::{OutputPin as _, StatefulOutputPin};
|
use embedded_hal::digital::v2::{OutputPin as _, StatefulOutputPin};
|
||||||
pub use hal::ieee802154;
|
|
||||||
pub use hal::pac::{
|
pub use hal::pac::{
|
||||||
interrupt, Interrupt, NVIC_PRIO_BITS, RTC0, UARTE1, uarte0::{
|
UARTE1, uarte0::{
|
||||||
baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}};
|
baudrate::BAUDRATE_A as Baudrate, config::PARITY_A as Parity}};
|
||||||
use hal::{
|
use hal::{
|
||||||
clocks::{self, Clocks},
|
|
||||||
gpio::{p0, Level, Output, Input, PullUp, Pin, Port, PushPull},
|
gpio::{p0, Level, Output, Input, PullUp, Pin, Port, PushPull},
|
||||||
rtc::{Rtc, RtcInterrupt},
|
|
||||||
timer::OneShot, prelude::InputPin,
|
timer::OneShot, prelude::InputPin,
|
||||||
};
|
};
|
||||||
|
|
||||||
use defmt;
|
use defmt;
|
||||||
use defmt_rtt as _; // global logger
|
use defmt_rtt as _; // global logger
|
||||||
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
peripheral::{POWER, USBD},
|
|
||||||
usbd::Ep0In,
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
mod errata;
|
|
||||||
pub mod peripheral;
|
|
||||||
pub mod usbd;
|
|
||||||
|
|
||||||
|
|
||||||
/// Components on the board
|
/// Components on the board
|
||||||
pub struct Board {
|
pub struct Board {
|
||||||
/// LEDs
|
/// LEDs
|
||||||
|
@ -49,15 +34,6 @@ pub struct Board {
|
||||||
// --- Exercise --- 🔼
|
// --- Exercise --- 🔼
|
||||||
/// Timer
|
/// Timer
|
||||||
pub timer: Timer,
|
pub timer: Timer,
|
||||||
|
|
||||||
/// Radio interface
|
|
||||||
pub radio: ieee802154::Radio<'static>,
|
|
||||||
/// USBD (Universal Serial Bus Device) peripheral
|
|
||||||
pub usbd: USBD,
|
|
||||||
/// POWER (Power Supply) peripheral
|
|
||||||
pub power: POWER,
|
|
||||||
/// USB control endpoint 0
|
|
||||||
pub ep0in: Ep0In,
|
|
||||||
// --- Exercise --- 🔽
|
// --- Exercise --- 🔽
|
||||||
/// uarte interface
|
/// uarte interface
|
||||||
pub uarte: Uarte,
|
pub uarte: Uarte,
|
||||||
|
@ -86,11 +62,7 @@ impl Led {
|
||||||
pub fn on(&mut self) {
|
pub fn on(&mut self) {
|
||||||
defmt::trace!(
|
defmt::trace!(
|
||||||
"setting P{}.{} low (LED on)",
|
"setting P{}.{} low (LED on)",
|
||||||
if self.inner.port() == Port::Port1 {
|
port_as_char(&self.inner.port()),
|
||||||
'1'
|
|
||||||
} else {
|
|
||||||
'0'
|
|
||||||
},
|
|
||||||
self.inner.pin()
|
self.inner.pin()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -102,11 +74,7 @@ impl Led {
|
||||||
pub fn off(&mut self) {
|
pub fn off(&mut self) {
|
||||||
defmt::trace!(
|
defmt::trace!(
|
||||||
"setting P{}.{} high (LED off)",
|
"setting P{}.{} high (LED off)",
|
||||||
if self.inner.port() == Port::Port1 {
|
port_as_char(&self.inner.port()),
|
||||||
'1'
|
|
||||||
} else {
|
|
||||||
'0'
|
|
||||||
},
|
|
||||||
self.inner.pin()
|
self.inner.pin()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -170,7 +138,8 @@ impl Timer {
|
||||||
pub fn wait(&mut self, duration: Duration) {
|
pub fn wait(&mut self, duration: Duration) {
|
||||||
defmt::trace!("blocking for {:?} ...", duration);
|
defmt::trace!("blocking for {:?} ...", duration);
|
||||||
|
|
||||||
// 1 cycle = 1 microsecond
|
// 1 cycle = 1 microsecond because the underlying HAL driver
|
||||||
|
// always sets the timer to 1 MHz.
|
||||||
const NANOS_IN_ONE_MICRO: u32 = 1_000;
|
const NANOS_IN_ONE_MICRO: u32 = 1_000;
|
||||||
let subsec_micros = duration.subsec_nanos() / NANOS_IN_ONE_MICRO;
|
let subsec_micros = duration.subsec_nanos() / NANOS_IN_ONE_MICRO;
|
||||||
if subsec_micros != 0 {
|
if subsec_micros != 0 {
|
||||||
|
@ -223,7 +192,7 @@ impl fmt::Write for Uarte {
|
||||||
fn write_str(&mut self, s: &str) -> fmt::Result {
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
// Copy all data into an on-stack buffer so we never try to EasyDMA from
|
// Copy all data into an on-stack buffer so we never try to EasyDMA from
|
||||||
// flash.
|
// flash.
|
||||||
let buf = &mut [0; 16][..];
|
let mut buf: [u8; 16] = [0; 16];
|
||||||
for block in s.as_bytes().chunks(16) {
|
for block in s.as_bytes().chunks(16) {
|
||||||
buf[..block.len()].copy_from_slice(block);
|
buf[..block.len()].copy_from_slice(block);
|
||||||
self.inner.write(&buf[..block.len()]).map_err(|_| fmt::Error)?;
|
self.inner.write(&buf[..block.len()]).map_err(|_| fmt::Error)?;
|
||||||
|
@ -238,42 +207,6 @@ impl fmt::Write for Uarte {
|
||||||
/// This return an `Err`or if called more than once
|
/// This return an `Err`or if called more than once
|
||||||
pub fn init() -> Result<Board, ()> {
|
pub fn init() -> Result<Board, ()> {
|
||||||
if let Some(periph) = hal::pac::Peripherals::take() {
|
if let Some(periph) = hal::pac::Peripherals::take() {
|
||||||
// NOTE(static mut) this branch runs at most once
|
|
||||||
static mut EP0IN_BUF: [u8; 64] = [0; 64];
|
|
||||||
static mut CLOCKS: Option<
|
|
||||||
Clocks<clocks::ExternalOscillator, clocks::ExternalOscillator, clocks::LfOscStarted>,
|
|
||||||
> = None;
|
|
||||||
|
|
||||||
defmt::debug!("Initializing the board");
|
|
||||||
|
|
||||||
let clocks = Clocks::new(periph.CLOCK);
|
|
||||||
let clocks = clocks.enable_ext_hfosc();
|
|
||||||
let clocks = clocks.set_lfclk_src_external(clocks::LfOscConfiguration::NoExternalNoBypass);
|
|
||||||
let clocks = clocks.start_lfclk();
|
|
||||||
let _clocks = clocks.enable_ext_hfosc();
|
|
||||||
// extend lifetime to `'static`
|
|
||||||
let clocks = unsafe { CLOCKS.get_or_insert(_clocks) };
|
|
||||||
|
|
||||||
defmt::debug!("Clocks configured");
|
|
||||||
|
|
||||||
let mut rtc = Rtc::new(periph.RTC0, 0).unwrap();
|
|
||||||
rtc.enable_interrupt(RtcInterrupt::Overflow, None);
|
|
||||||
rtc.enable_counter();
|
|
||||||
// NOTE(unsafe) because this crate defines the `#[interrupt] fn RTC0` interrupt handler,
|
|
||||||
// RTIC cannot manage that interrupt (trying to do so results in a linker error). Thus it
|
|
||||||
// is the task of this crate to mask/unmask the interrupt in a safe manner.
|
|
||||||
//
|
|
||||||
// Because the RTC0 interrupt handler does *not* access static variables through a critical
|
|
||||||
// section (that disables interrupts) this `unmask` operation cannot break critical sections
|
|
||||||
// and thus won't lead to undefined behavior (e.g. torn reads/writes)
|
|
||||||
//
|
|
||||||
// the preceding `enable_conuter` method consumes the `rtc` value. This is a semantic move
|
|
||||||
// of the RTC0 peripheral from this function (which can only be called at most once) to the
|
|
||||||
// interrupt handler (where the peripheral is accessed without any synchronization
|
|
||||||
// mechanism)
|
|
||||||
unsafe { NVIC::unmask(Interrupt::RTC0) };
|
|
||||||
|
|
||||||
defmt::debug!("RTC started");
|
|
||||||
|
|
||||||
let pins = p0::Parts::new(periph.P0);
|
let pins = p0::Parts::new(periph.P0);
|
||||||
|
|
||||||
|
@ -308,18 +241,6 @@ pub fn init() -> Result<Board, ()> {
|
||||||
let uarte = hal::uarte::Uarte::new(periph.UARTE0, pins, Parity::INCLUDED, Baudrate::BAUD115200);
|
let uarte = hal::uarte::Uarte::new(periph.UARTE0, pins, Parity::INCLUDED, Baudrate::BAUD115200);
|
||||||
// --- Exercise --- 🔼
|
// --- Exercise --- 🔼
|
||||||
|
|
||||||
// Radio
|
|
||||||
let radio = {
|
|
||||||
let mut radio = ieee802154::Radio::init(periph.RADIO, clocks);
|
|
||||||
|
|
||||||
// set TX power to its maximum value
|
|
||||||
radio.set_txpower(ieee802154::TxPower::Pos8dBm);
|
|
||||||
defmt::debug!(
|
|
||||||
"Radio initialized and configured with TX power set to the maximum value"
|
|
||||||
);
|
|
||||||
radio
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(Board {
|
Ok(Board {
|
||||||
leds: Leds {
|
leds: Leds {
|
||||||
led_1: Led { inner: led_1 },
|
led_1: Led { inner: led_1 },
|
||||||
|
@ -336,12 +257,9 @@ pub fn init() -> Result<Board, ()> {
|
||||||
b_4: Button { inner: b_4},
|
b_4: Button { inner: b_4},
|
||||||
},
|
},
|
||||||
// --- Exercise --- 🔼
|
// --- Exercise --- 🔼
|
||||||
radio,
|
|
||||||
timer: Timer { inner: timer },
|
timer: Timer { inner: timer },
|
||||||
usbd: periph.USBD,
|
|
||||||
power: periph.POWER,
|
|
||||||
ep0in: unsafe { Ep0In::new(&mut EP0IN_BUF) },
|
|
||||||
|
|
||||||
// --- Exercise --- 🔽
|
// --- Exercise --- 🔽
|
||||||
uarte: Uarte { inner: uarte },
|
uarte: Uarte { inner: uarte },
|
||||||
// --- Exercise --- 🔼
|
// --- Exercise --- 🔼
|
||||||
|
@ -351,19 +269,6 @@ pub fn init() -> Result<Board, ()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Counter of OVERFLOW events -- an OVERFLOW occurs every (1<<24) ticks
|
|
||||||
static OVERFLOWS: AtomicU32 = AtomicU32::new(0);
|
|
||||||
|
|
||||||
// NOTE this will run at the highest priority, higher priority than RTIC tasks
|
|
||||||
#[interrupt]
|
|
||||||
fn RTC0() {
|
|
||||||
let curr = OVERFLOWS.load(Ordering::Relaxed);
|
|
||||||
OVERFLOWS.store(curr + 1, Ordering::Relaxed);
|
|
||||||
|
|
||||||
// clear the EVENT register
|
|
||||||
unsafe { core::mem::transmute::<_, RTC0>(()).events_ovrflw.reset() }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Exits the application when the program is executed through the `probe-run` Cargo runner
|
/// Exits the application when the program is executed through the `probe-run` Cargo runner
|
||||||
pub fn exit() -> ! {
|
pub fn exit() -> ! {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -384,49 +289,11 @@ pub fn exit() -> ! {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the time elapsed since the call to the `dk::init` function
|
// Helper functions
|
||||||
///
|
|
||||||
/// The clock that is read to compute this value has a resolution of 30 microseconds.
|
|
||||||
///
|
|
||||||
/// Calling this function before calling `dk::init` will return a value of `0` nanoseconds.
|
|
||||||
pub fn uptime() -> Duration {
|
|
||||||
// here we are going to perform a 64-bit read of the number of ticks elapsed
|
|
||||||
//
|
|
||||||
// a 64-bit load operation cannot performed in a single instruction so the operation can be
|
|
||||||
// preempted by the RTC0 interrupt handler (which increases the OVERFLOWS counter)
|
|
||||||
//
|
|
||||||
// the loop below will load both the lower and upper parts of the 64-bit value while preventing
|
|
||||||
// the issue of mixing a low value with an "old" high value -- note that, due to interrupts, an
|
|
||||||
// arbitrary amount of time may elapse between the `hi1` load and the `low` load
|
|
||||||
let overflows = &OVERFLOWS as *const AtomicU32 as *const u32;
|
|
||||||
let ticks = loop {
|
|
||||||
unsafe {
|
|
||||||
// NOTE volatile is used to order these load operations among themselves
|
|
||||||
let hi1 = overflows.read_volatile();
|
|
||||||
let low = core::mem::transmute::<_, RTC0>(())
|
|
||||||
.counter
|
|
||||||
.read()
|
|
||||||
.counter()
|
|
||||||
.bits();
|
|
||||||
let hi2 = overflows.read_volatile();
|
|
||||||
|
|
||||||
if hi1 == hi2 {
|
fn port_as_char(port: &Port) -> char {
|
||||||
break u64::from(low) | (u64::from(hi1) << 24);
|
match port {
|
||||||
}
|
Port::Port0 => '0',
|
||||||
}
|
Port::Port1 => '1',
|
||||||
};
|
}
|
||||||
|
|
||||||
// 2**15 ticks = 1 second
|
|
||||||
let freq = 1 << 15;
|
|
||||||
let secs = ticks / freq;
|
|
||||||
// subsec ticks
|
|
||||||
let ticks = (ticks % freq) as u32;
|
|
||||||
// one tick is equal to `1e9 / 32768` nanos
|
|
||||||
// the fraction can be reduced to `1953125 / 64`
|
|
||||||
// which can be further decomposed as `78125 * (5 / 4) * (5 / 4) * (1 / 4)`.
|
|
||||||
// Doing the operation this way we can stick to 32-bit arithmetic without overflowing the value
|
|
||||||
// at any stage
|
|
||||||
let nanos =
|
|
||||||
(((ticks % 32768).wrapping_mul(78125) >> 2).wrapping_mul(5) >> 2).wrapping_mul(5) >> 2;
|
|
||||||
Duration::new(secs, nanos as u32)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
//! Low level access to the nRF52840 peripheral
|
|
||||||
|
|
||||||
pub use hal::pac::{POWER, USBD};
|
|
|
@ -1,231 +0,0 @@
|
||||||
//! USBD peripheral
|
|
||||||
|
|
||||||
use core::sync::atomic::{self, Ordering};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
errata,
|
|
||||||
peripheral::{POWER, USBD},
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Endpoint IN 0
|
|
||||||
pub struct Ep0In {
|
|
||||||
buffer: &'static mut [u8; 64],
|
|
||||||
busy: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Ep0In {
|
|
||||||
/// # Safety
|
|
||||||
/// Must be created at most once (singleton)
|
|
||||||
pub(crate) unsafe fn new(buffer: &'static mut [u8; 64]) -> Self {
|
|
||||||
Self {
|
|
||||||
buffer,
|
|
||||||
busy: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Starts a data transfer over endpoint 0
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// - This function panics if the last transfer was not finished by calling the `end` function
|
|
||||||
/// - This function panics if `bytes` is larger than the maximum packet size (64 bytes)
|
|
||||||
pub fn start(&mut self, bytes: &[u8], usbd: &USBD) {
|
|
||||||
assert!(!self.busy, "EP0IN: last transfer has not completed");
|
|
||||||
assert!(
|
|
||||||
bytes.len() <= self.buffer.len(),
|
|
||||||
"EP0IN: multi-packet data transfers are not supported"
|
|
||||||
);
|
|
||||||
|
|
||||||
let n = bytes.len();
|
|
||||||
self.buffer[..n].copy_from_slice(bytes);
|
|
||||||
|
|
||||||
// use a "shortcut" to issue a status stage after the data transfer is complete
|
|
||||||
usbd.shorts
|
|
||||||
.modify(|_, w| w.ep0datadone_ep0status().set_bit());
|
|
||||||
usbd.epin0
|
|
||||||
.maxcnt
|
|
||||||
.write(|w| unsafe { w.maxcnt().bits(n as u8) });
|
|
||||||
usbd.epin0
|
|
||||||
.ptr
|
|
||||||
.write(|w| unsafe { w.ptr().bits(self.buffer.as_ptr() as u32) });
|
|
||||||
|
|
||||||
self.busy = true;
|
|
||||||
|
|
||||||
defmt::println!("EP0IN: start {}B transfer", n);
|
|
||||||
|
|
||||||
// start DMA transfer
|
|
||||||
dma_start();
|
|
||||||
usbd.tasks_startepin[0].write(|w| w.tasks_startepin().set_bit());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Completes a data transfer
|
|
||||||
///
|
|
||||||
/// This function must be called after the EP0DATADONE event is raised
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// This function panics if called before `start` or before the EP0DATADONE event is raised by
|
|
||||||
/// the hardware
|
|
||||||
pub fn end(&mut self, usbd: &USBD) {
|
|
||||||
if usbd.events_ep0datadone.read().bits() == 0 {
|
|
||||||
panic!("Ep0In.end called before the EP0DATADONE event was raised");
|
|
||||||
} else {
|
|
||||||
// DMA transfer complete
|
|
||||||
dma_end();
|
|
||||||
usbd.events_ep0datadone.reset();
|
|
||||||
|
|
||||||
self.busy = false;
|
|
||||||
defmt::println!("EP0IN: transfer done");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// memory barrier to synchronize the start of a DMA transfer (which will run in parallel) with the
|
|
||||||
// caller's memory operations
|
|
||||||
//
|
|
||||||
// This function call *must* be *followed* by a memory *store* operation. Memory operations that
|
|
||||||
// *precede* this function call will *not* be moved, by the compiler or the instruction pipeline, to
|
|
||||||
// *after* the function call.
|
|
||||||
fn dma_start() {
|
|
||||||
atomic::fence(Ordering::Release);
|
|
||||||
}
|
|
||||||
|
|
||||||
// memory barrier to synchronize the end of a DMA transfer (which ran in parallel) to the caller's
|
|
||||||
// memory operations
|
|
||||||
//
|
|
||||||
// This function call *must* be *preceded* by a memory *load* operation. Memory operations that
|
|
||||||
// *follow* this function call will *not* be moved, by the compiler or the instruction pipeline, to
|
|
||||||
// *before* the function call.
|
|
||||||
fn dma_end() {
|
|
||||||
atomic::fence(Ordering::Acquire);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initializes the USBD peripheral
|
|
||||||
// NOTE will be called from user code; at that point the high frequency clock source has already
|
|
||||||
// been configured to use to the external crystal
|
|
||||||
// Reference: section 6.35.4 of the nRF52840 Product Specification
|
|
||||||
pub fn init(power: POWER, usbd: &USBD) {
|
|
||||||
let mut once = true;
|
|
||||||
|
|
||||||
// wait until the USB cable has been connected
|
|
||||||
while power.events_usbdetected.read().bits() == 0 {
|
|
||||||
if once {
|
|
||||||
defmt::println!("waiting for USB connection on port J3");
|
|
||||||
once = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
power.events_usbdetected.reset();
|
|
||||||
|
|
||||||
// workaround silicon bug
|
|
||||||
unsafe { errata::e187a() }
|
|
||||||
// enable the USB peripheral
|
|
||||||
usbd.enable.write(|w| w.enable().set_bit());
|
|
||||||
|
|
||||||
// wait for the peripheral to signal it has reached the READY state
|
|
||||||
while usbd.eventcause.read().ready().bit_is_clear() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// write 1 to clear the flag
|
|
||||||
usbd.eventcause.write(|w| w.ready().set_bit());
|
|
||||||
|
|
||||||
// if EVENTCAUSE is all zeroes then also clear the USBEVENT register
|
|
||||||
if usbd.eventcause.read().bits() == 0 {
|
|
||||||
usbd.events_usbevent.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
// complete the silicon bug workaround
|
|
||||||
unsafe { errata::e187b() }
|
|
||||||
|
|
||||||
// also need to wait for the USB power supply regulator to stabilize
|
|
||||||
while power.events_usbpwrrdy.read().bits() == 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
power.events_usbpwrrdy.reset();
|
|
||||||
|
|
||||||
// before returning unmask the relevant interrupts
|
|
||||||
usbd.intenset.write(|w| {
|
|
||||||
w.ep0datadone().set_bit();
|
|
||||||
w.ep0setup().set_bit();
|
|
||||||
w.usbreset().set_bit()
|
|
||||||
});
|
|
||||||
|
|
||||||
// enable the D+ line pull-up
|
|
||||||
usbd.usbpullup.write(|w| w.connect().set_bit());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stalls endpoint 0
|
|
||||||
pub fn ep0stall(usbd: &USBD) {
|
|
||||||
usbd.tasks_ep0stall.write(|w| w.tasks_ep0stall().set_bit());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// USBD.EVENTS registers mapped to an enum
|
|
||||||
#[derive(Debug, defmt::Format)]
|
|
||||||
pub enum Event {
|
|
||||||
/// `EVENTS_USBRESET` register was active
|
|
||||||
UsbReset,
|
|
||||||
|
|
||||||
/// `EVENTS_EP0DATADONE` register was active
|
|
||||||
UsbEp0DataDone,
|
|
||||||
|
|
||||||
/// `EVENTS_EP0SETUP` register was active
|
|
||||||
UsbEp0Setup,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the next unhandled USB event; returns none if there's no event to handle
|
|
||||||
///
|
|
||||||
/// NOTE this function will clear the corresponding the EVENT register (*) so the caller should
|
|
||||||
/// handle the returned event properly. Expect for USBEVENT and EP0DATADONE
|
|
||||||
pub fn next_event(usbd: &USBD) -> Option<Event> {
|
|
||||||
if usbd.events_usbreset.read().bits() != 0 {
|
|
||||||
usbd.events_usbreset.reset();
|
|
||||||
|
|
||||||
return Some(Event::UsbReset);
|
|
||||||
}
|
|
||||||
|
|
||||||
if usbd.events_ep0datadone.read().bits() != 0 {
|
|
||||||
// this will be cleared by the `Ep0In.end` method
|
|
||||||
// usbd.events_ep0datadone.reset();
|
|
||||||
|
|
||||||
return Some(Event::UsbEp0DataDone);
|
|
||||||
}
|
|
||||||
|
|
||||||
if usbd.events_ep0setup.read().bits() != 0 {
|
|
||||||
usbd.events_ep0setup.reset();
|
|
||||||
|
|
||||||
return Some(Event::UsbEp0Setup);
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the BMREQUESTTYPE register and returns the 8-bit BMREQUESTTYPE component of a setup packet
|
|
||||||
pub fn bmrequesttype(usbd: &USBD) -> u8 {
|
|
||||||
// read the 32-bit register and extract the least significant byte
|
|
||||||
// (the alternative is to read the 3 bitfields of the register and merge them into one byte)
|
|
||||||
usbd.bmrequesttype.read().bits() as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the BREQUEST register and returns the 8-bit BREQUEST component of a setup packet
|
|
||||||
pub fn brequest(usbd: &USBD) -> u8 {
|
|
||||||
usbd.brequest.read().brequest().bits()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the WLENGTHL and WLENGTHH registers and returns the 16-bit WLENGTH component of a setup packet
|
|
||||||
pub fn wlength(usbd: &USBD) -> u16 {
|
|
||||||
u16::from(usbd.wlengthl.read().wlengthl().bits())
|
|
||||||
| u16::from(usbd.wlengthh.read().wlengthh().bits()) << 8
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the WINDEXL and WINDEXH registers and returns the 16-bit WINDEX component of a setup packet
|
|
||||||
pub fn windex(usbd: &USBD) -> u16 {
|
|
||||||
u16::from(usbd.windexl.read().windexl().bits())
|
|
||||||
| u16::from(usbd.windexh.read().windexh().bits()) << 8
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reads the WVALUEL and WVALUEH registers and returns the 16-bit WVALUE component of a setup packet
|
|
||||||
pub fn wvalue(usbd: &USBD) -> u16 {
|
|
||||||
u16::from(usbd.wvaluel.read().wvaluel().bits())
|
|
||||||
| u16::from(usbd.wvalueh.read().wvalueh().bits()) << 8
|
|
||||||
}
|
|
Loading…
Reference in a new issue