- dk: API docs

- advanced: adapt to changes in usb2 dependency
- dk: add reminder to connect the USB cable to port J3
This commit is contained in:
Jorge Aparicio 2020-06-09 18:17:30 +02:00
parent e212e819b9
commit 31c331c1a3
8 changed files with 99 additions and 55 deletions

View file

@ -60,7 +60,7 @@ version = "0.1.0"
[[package]]
name = "cortex-m"
version = "0.6.2"
source = "git+https://github.com/rust-embedded/cortex-m#3136e01e708413774d7be2868705e1782c910027"
source = "git+https://github.com/rust-embedded/cortex-m#c9c7539233954822a6132f4bc13e5763371b5cb2"
dependencies = [
"bare-metal",
"bitfield",
@ -118,6 +118,7 @@ name = "dk"
version = "0.1.0"
dependencies = [
"cortex-m",
"cortex-m-rt",
"embedded-hal",
"log",
"nrf52840-hal",
@ -143,6 +144,7 @@ dependencies = [
"cortex-m-rt",
"cortex-m-rtfm",
"dk",
"heapless",
"log",
"panic-log",
"quote",
@ -201,9 +203,9 @@ dependencies = [
[[package]]
name = "indexmap"
version = "1.3.2"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "076f042c5b7b98f31d205f1249267e12a6518c1481e9dae9764af19b707d2292"
checksum = "c398b2b113b55809ceb9ee3e753fcbac793f1956663f3c36549c1346015c2afe"
dependencies = [
"autocfg",
]
@ -226,7 +228,7 @@ checksum = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc"
[[package]]
name = "nrf-hal-common"
version = "0.10.0"
source = "git+https://github.com/japaric/nrf-hal?branch=radio#443662d20591c3bef26a4fff2de03de90fec51aa"
source = "git+https://github.com/japaric/nrf-hal?branch=radio#d624e80e5724e4709081ed65abaf63271fe1eca7"
dependencies = [
"cast",
"cortex-m",
@ -241,7 +243,7 @@ dependencies = [
[[package]]
name = "nrf52840-hal"
version = "0.10.0"
source = "git+https://github.com/japaric/nrf-hal?branch=radio#443662d20591c3bef26a4fff2de03de90fec51aa"
source = "git+https://github.com/japaric/nrf-hal?branch=radio#d624e80e5724e4709081ed65abaf63271fe1eca7"
dependencies = [
"cast",
"cortex-m",
@ -274,18 +276,18 @@ dependencies = [
[[package]]
name = "proc-macro2"
version = "1.0.17"
version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101"
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.6"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
dependencies = [
"proc-macro2",
]
@ -362,9 +364,9 @@ checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
[[package]]
name = "syn"
version = "1.0.25"
version = "1.0.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f14a640819f79b72a710c0be059dce779f9339ae046c8bef12c361d56702146f"
checksum = "93a56fabc59dce20fe48b6c832cc249c713e7ed88fa28b0ee0a3bfcaae5fe4e2"
dependencies = [
"proc-macro2",
"quote",
@ -396,7 +398,7 @@ version = "0.1.0"
[[package]]
name = "usb2"
version = "0.0.0"
source = "git+https://github.com/japaric/usb2#c2b8118e667026f690c847782ebc5809aba749fe"
source = "git+https://github.com/japaric/usb2#6a8c4b1ed0db005dcf19694684524b33cc04abd2"
[[package]]
name = "vcell"

View file

@ -16,6 +16,7 @@ cortex-m-rt = "0.6.12"
# TODO switch to RTIC before public release
cortex-m-rtfm = "0.5.1"
dk = { path = "../../boards/dk" }
heapless = "0.5.5"
log = "0.4.8"
panic-log = { path = "../../common/panic-log" }
usb = { path = "../common/usb" }

View file

@ -16,15 +16,10 @@ const APP: () = {
struct Resources {
usbd: USBD,
ep0in: Ep0In,
#[init([0; 64])]
buffer: [u8; 64],
}
#[init(resources = [buffer])]
fn init(cx: init::Context) -> init::LateResources {
let buffer: &'static mut [u8; 64] = cx.resources.buffer;
let ep0in = Ep0In::new(buffer);
#[init]
fn init(_cx: init::Context) -> init::LateResources {
let board = dk::init().unwrap();
usbd::init(board.power, &board.usbd);
@ -32,8 +27,8 @@ const APP: () = {
usbd::connect(&board.usbd);
init::LateResources {
ep0in: board.ep0in,
usbd: board.usbd,
ep0in,
}
}

View file

@ -15,18 +15,13 @@ use usb2::{GetDescriptor as Descriptor, StandardRequest as Request}; // crates.i
#[rtfm::app(device = dk)]
const APP: () = {
struct Resources {
#[init([0; 64])]
buffer: [u8; 64],
usbd: USBD,
ep0in: Ep0In,
state: State,
}
#[init(resources = [buffer])]
fn init(cx: init::Context) -> init::LateResources {
let buffer: &'static mut [u8; 64] = cx.resources.buffer;
let ep0in = Ep0In::new(buffer);
#[init]
fn init(_cx: init::Context) -> init::LateResources {
let board = dk::init().unwrap();
usbd::init(board.power, &board.usbd);
@ -36,7 +31,7 @@ const APP: () = {
init::LateResources {
usbd: board.usbd,
state: State::Default,
ep0in,
ep0in: board.ep0in,
}
}
@ -72,7 +67,7 @@ fn on_event(usbd: &USBD, ep0in: &mut Ep0In, state: &mut State, event: Event) {
}
}
fn ep0setup(usbd: &USBD, ep0in: &mut Ep0In, state: &mut State) -> Result<(), ()> {
fn ep0setup(usbd: &USBD, ep0in: &mut Ep0In, _state: &mut State) -> Result<(), ()> {
let bmrequesttype = usbd.bmrequesttype.read().bits() as u8;
let brequest = usbd.brequest.read().brequest().bits();
let wlength = usbd::wlength(usbd);

View file

@ -1,6 +1,8 @@
#![no_main]
#![no_std]
use core::num::NonZeroU8;
use dk::{
peripheral::USBD,
usbd::{self, Ep0In, Event},
@ -18,10 +20,7 @@ const APP: () = {
#[init]
fn init(_cx: init::Context) -> init::LateResources {
static mut BUFFER: [u8; 64] = [0; 64];
let board = dk::init().unwrap();
let ep0in = Ep0In::new(BUFFER);
usbd::init(board.power, &board.usbd);
@ -30,7 +29,7 @@ const APP: () = {
init::LateResources {
usbd: board.usbd,
state: State::Default,
ep0in,
ep0in: board.ep0in,
}
}
@ -106,9 +105,11 @@ fn ep0setup(usbd: &USBD, state: &mut State, ep0in: &mut Ep0In) -> Result<(), ()>
GetDescriptor::Configuration { index } => {
if index == 0 {
let desc = usb2::configuration::Descriptor {
let mut full_desc = heapless::Vec::<u8, heapless::consts::U64>::new();
let conf_desc = usb2::configuration::Descriptor {
wTotalLength: usb2::configuration::Descriptor::SIZE.into(),
bNumInterfaces: 0,
bNumInterfaces: NonZeroU8::new(1).unwrap(),
bConfigurationValue: core::num::NonZeroU8::new(CONFIG_VAL).unwrap(),
iConfiguration: None,
bmAttributes: usb2::configuration::bmAttributes {
@ -118,8 +119,22 @@ fn ep0setup(usbd: &USBD, state: &mut State, ep0in: &mut Ep0In) -> Result<(), ()>
bMaxPower: 250, // 500 mA
};
let bytes = desc.bytes();
ep0in.start(&bytes[..core::cmp::min(bytes.len(), length.into())], usbd)?;
let iface_desc = usb2::interface::Descriptor {
bInterfaceNumber: 0,
bAlternativeSetting: 0,
bNumEndpoints: 0,
bInterfaceClass: 0,
bInterfaceSubClass: 0,
bInterfaceProtocol: 0,
iInterface: None,
};
full_desc.extend_from_slice(&conf_desc.bytes()).unwrap();
full_desc.extend_from_slice(&iface_desc.bytes()).unwrap();
ep0in.start(
&full_desc[..core::cmp::min(full_desc.len(), length.into())],
usbd,
)?;
} else {
// out of bounds access: stall the endpoint
return Err(());

View file

@ -1,5 +1,7 @@
//! Hardware Abstraction Layer (HAL) for the nRF52840 Development Kit
#![deny(missing_docs)]
#![deny(warnings)]
#![no_std]
use core::{
@ -21,7 +23,10 @@ use hal::{
use log::{LevelFilter, Log};
use rtt_target::{rprintln, rtt_init_print};
use crate::peripheral::{POWER, USBD};
use crate::{
peripheral::{POWER, USBD},
usbd::Ep0In,
};
mod errata;
pub mod peripheral;
@ -29,13 +34,20 @@ pub mod usbd;
/// Components on the board
pub struct Board {
/// LEDs
pub leds: Leds,
/// Radio interface
// TODO put behind feature flag (off in advanced workshop)
pub radio: Radio<'static>,
/// Timer
pub timer: Timer,
/// USBD (Universal Serial Bus Device) peripheral
// TODO put behind feature flag (off in beginner workshop)
pub usbd: USBD,
/// POWER (Power Supply) peripheral
pub power: POWER,
/// USB control endpoint 0
pub ep0in: Ep0In,
}
/// All LEDs on the board
@ -149,7 +161,8 @@ pub fn init() -> Result<Board, ()> {
cortex_m::Peripherals::take(),
hal::target::Peripherals::take(),
) {
// NOTE(unsafe) this branch runs at most once
// NOTE(static mut) this branch runs at most once
static mut EP0IN_BUF: [u8; 64] = [0; 64];
static mut CLOCKS: Option<Clocks<ExternalOscillator, ExternalOscillator, LfOscStarted>> =
None;
@ -210,6 +223,7 @@ pub fn init() -> Result<Board, ()> {
timer: Timer { inner: timer },
usbd: periph.USBD,
power: periph.POWER,
ep0in: unsafe { Ep0In::new(&mut EP0IN_BUF) },
})
} else {
Err(())

View file

@ -1 +1,3 @@
//! Low level access to the nRF52840 peripheral
pub use hal::target::{POWER, USBD};

View file

@ -1,6 +1,6 @@
use core::sync::atomic::{self, Ordering};
//! USBD peripheral
use cortex_m::asm;
use core::sync::atomic::{self, Ordering};
use crate::{
errata,
@ -14,17 +14,23 @@ pub struct Ep0In {
}
impl Ep0In {
pub fn new(buffer: &'static mut [u8; 64]) -> Self {
/// # 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
pub fn start(&mut self, bytes: &[u8], usbd: &USBD) -> Result<(), ()> {
if self.busy {
log::error!("EP0IN: last transfer not completed");
return Err(());
panic!("EP0IN: last transfer has not completed");
}
if bytes.len() > self.buffer.len() {
@ -56,12 +62,17 @@ impl Ep0In {
Ok(())
}
/// 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 {
log::error!("Ep0In.end called before the EP0DATADONE event was raised");
loop {
asm::bkpt();
}
panic!("Ep0In.end called before the EP0DATADONE event was raised");
} else {
// DMA transfer complete
dma_end();
@ -77,7 +88,7 @@ impl Ep0In {
//
// This function call *must* be *followed* by a memory *store* operation. Memory operations that
// follow this function call will *not* be moved, by the compiler or the instruction pipeline, to
// *after* the function call.
// *after* the function call.
fn dma_start() {
atomic::fence(Ordering::Release);
}
@ -87,21 +98,28 @@ fn dma_start() {
//
// 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.
// *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 has been connected
while power.events_usbdetected.read().bits() == 0 {
power.events_usbdetected.reset();
if once {
log::info!("waiting for USB connection on port J3");
once = false;
}
continue;
}
power.events_usbdetected.reset();
// workaround silicon bug
unsafe { errata::e187a() }
@ -147,6 +165,7 @@ pub fn disconnect(usbd: &USBD) {
usbd.usbpullup.reset();
}
/// Stalls endpoint 0
pub fn ep0stall(usbd: &USBD) {
usbd.tasks_ep0stall.write(|w| w.tasks_ep0stall().set_bit());
}
@ -176,7 +195,7 @@ pub fn next_event(usbd: &USBD) -> Option<Event> {
}
if usbd.events_ep0datadone.read().bits() != 0 {
// this will be cleared elsewhere
// this will be cleared by the `Ep0In.end` method
// usbd.events_ep0datadone.reset();
return Some(Event::UsbEp0DataDone);
@ -195,21 +214,22 @@ pub fn next_event(usbd: &USBD) -> Option<Event> {
pub fn todo(usbd: &USBD) {
disconnect(usbd);
log::error!("unimplemented");
loop {
asm::bkpt()
}
crate::exit()
}
/// 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