mirror of
https://github.com/ferrous-systems/embedded-trainings-2020.git
synced 2025-01-25 07:18:08 +00:00
Merge pull request #140 from ferrous-systems/111-use-defmt-logging
111 use defmt logging
This commit is contained in:
commit
d63f8dfd65
63 changed files with 1347 additions and 1053 deletions
|
@ -6,4 +6,6 @@ name = "usb"
|
|||
version = "0.0.0"
|
||||
|
||||
[dependencies]
|
||||
log = "0.4.8"
|
||||
defmt = "0.3.0"
|
||||
|
||||
|
||||
|
|
|
@ -90,6 +90,7 @@ impl Request {
|
|||
Err(())
|
||||
}
|
||||
} else {
|
||||
defmt::println!("unhandled case in `Request` parser");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ impl Request {
|
|||
Err(())
|
||||
}
|
||||
} else {
|
||||
log::warn!("unhandled case in `Request` parser");
|
||||
defmt::println!("unhandled case in `Request` parser");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
# (..)
|
||||
rustflags = [
|
||||
"-C", "linker=flip-link", # adds stack overflow protection
|
||||
"-C", "link-arg=-Tdefmt.x", # defmt support
|
||||
# (..)
|
||||
]
|
||||
|
||||
|
|
|
@ -12,20 +12,24 @@ usb2 = "0.0.1"
|
|||
|
||||
[dependencies]
|
||||
consts = { path = "../common/consts" }
|
||||
cortex-m = "0.6.4"
|
||||
cortex-m-rt = "0.6.13"
|
||||
cortex-m-rtic = "0.5.1"
|
||||
cortex-m = "0.7.3"
|
||||
cortex-m-rt = "0.7.1"
|
||||
cortex-m-rtic = "1.0.0"
|
||||
defmt = "0.3.0"
|
||||
defmt-rtt = "0.3.1"
|
||||
dk = { path = "../../boards/dk", features = ["advanced"] }
|
||||
heapless = "0.5.5"
|
||||
log = "0.4.8"
|
||||
panic-log = { path = "../../common/panic-log" }
|
||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||
usb = { path = "../common/usb" }
|
||||
usb2 = "0.0.1"
|
||||
|
||||
[dependencies.heapless]
|
||||
version = "0.7.9"
|
||||
features = ["defmt-impl"]
|
||||
|
||||
# optimize code in both profiles
|
||||
[profile.dev]
|
||||
codegen-units = 1
|
||||
debug = 1
|
||||
debug = 2
|
||||
debug-assertions = true # !
|
||||
incremental = false
|
||||
lto = "fat"
|
||||
|
@ -40,3 +44,4 @@ incremental = false
|
|||
lto = "fat"
|
||||
opt-level = 3
|
||||
overflow-checks = false
|
||||
|
||||
|
|
|
@ -1,13 +1,21 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m::asm;
|
||||
use panic_log as _; // panic handler
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
use cortex_m::asm;
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
#[init]
|
||||
fn init(_cx: init::Context) {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
// `POWER` is a peripheral, or register block
|
||||
|
@ -22,7 +30,7 @@ const APP: () = {
|
|||
w.usbdetected().set_bit()
|
||||
});
|
||||
|
||||
log::info!("USBDETECTED interrupt enabled");
|
||||
defmt::println!("USBDETECTED interrupt enabled");
|
||||
|
||||
// read the whole 32-bit usb supply register
|
||||
// the `read()` method returns a reader which can then be used to access the register content
|
||||
|
@ -30,19 +38,25 @@ const APP: () = {
|
|||
// (the layout of the USBREGSTATUS register can be found in section 5.3.7.13 of the PS)
|
||||
let regstatus: u32 = power.usbregstatus.read().bits();
|
||||
// ^^^^ complete register content
|
||||
log::info!("USBREGSTATUS: {:b}", regstatus);
|
||||
defmt::println!("USBREGSTATUS: {:b}", regstatus);
|
||||
|
||||
// read the 1-bit VBUSDETECT field that is part of the USBREGSTATUS register content
|
||||
// to show that its contents reflect our usb connection status
|
||||
// (the USBDETECTED event that will trigger `on_power_event()` is derived from this information)
|
||||
let vbusdetect: bool = power.usbregstatus.read().vbusdetect().bits();
|
||||
// ^^^^^^^^^^ bitfield name
|
||||
log::info!("USBREGSTATUS.VBUSDETECT: {}", vbusdetect);
|
||||
defmt::println!("USBREGSTATUS.VBUSDETECT: {}", vbusdetect);
|
||||
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources {},
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[idle]
|
||||
fn main(_cx: main::Context) -> ! {
|
||||
log::info!("idle: going to sleep");
|
||||
fn idle(_cx: idle::Context) -> ! {
|
||||
defmt::println!("idle: going to sleep");
|
||||
|
||||
// sleep in the background
|
||||
loop {
|
||||
|
@ -52,7 +66,7 @@ const APP: () = {
|
|||
|
||||
#[task(binds = POWER_CLOCK)]
|
||||
fn on_power_event(_cx: on_power_event::Context) {
|
||||
log::info!("POWER event occurred");
|
||||
dk::exit()
|
||||
defmt::println!("POWER event occurred");
|
||||
asm::bkpt();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
|
||||
use cortex_m::asm;
|
||||
use cortex_m_rt::entry;
|
||||
use panic_log as _; // panic handler
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
// board initialization
|
||||
dk::init().unwrap();
|
||||
|
||||
log::info!("Hello, world!");
|
||||
defmt::println!("Hello, world!");
|
||||
|
||||
loop {
|
||||
asm::bkpt();
|
||||
|
|
|
@ -1,53 +1,63 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m::asm;
|
||||
use dk::peripheral::POWER;
|
||||
use panic_log as _; // panic handler
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
use cortex_m::asm;
|
||||
use dk::peripheral::POWER;
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {
|
||||
power: POWER,
|
||||
counter: usize, // <- new resource
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
|
||||
#[init]
|
||||
fn init(_cx: init::Context) -> init::LateResources {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
let power = board.power;
|
||||
|
||||
power.intenset.write(|w| w.usbdetected().set_bit());
|
||||
|
||||
log::info!("USBDETECTED interrupt enabled");
|
||||
defmt::println!("USBDETECTED interrupt enabled");
|
||||
|
||||
init::LateResources {
|
||||
power,
|
||||
counter: 0, // <- initialize the new resource
|
||||
}
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources {
|
||||
power,
|
||||
counter: 0, // <- initialize the new resource
|
||||
},
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[idle]
|
||||
fn main(_cx: main::Context) -> ! {
|
||||
fn idle(_cx: idle::Context) -> ! {
|
||||
loop {
|
||||
log::info!("idle: going to sleep");
|
||||
defmt::println!("idle: going to sleep");
|
||||
asm::wfi();
|
||||
log::info!("idle: woke up");
|
||||
defmt::println!("idle: woke up");
|
||||
}
|
||||
}
|
||||
|
||||
#[task(binds = POWER_CLOCK, resources = [power, counter])]
|
||||
// ^^^^^^^ we want to access the resource from here
|
||||
#[task(binds = POWER_CLOCK, local = [power, counter])]
|
||||
// ^^^^^^^ we want to access the resource from here
|
||||
fn on_power_event(cx: on_power_event::Context) {
|
||||
log::debug!("POWER event occurred");
|
||||
defmt::debug!("POWER event occurred");
|
||||
|
||||
let power = cx.resources.power;
|
||||
let counter = cx.resources.counter;
|
||||
let power = cx.local.power;
|
||||
let counter = cx.local.counter;
|
||||
|
||||
*counter += 1;
|
||||
let n = *counter;
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"on_power_event: cable connected {} time{}",
|
||||
n,
|
||||
if n != 1 { "s" } else { "" }
|
||||
|
@ -56,4 +66,4 @@ const APP: () = {
|
|||
// clear the interrupt flag; otherwise this task will run again after it returns
|
||||
power.events_usbdetected.reset();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,47 +1,55 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m::asm;
|
||||
use dk::peripheral::POWER;
|
||||
use panic_log as _; // panic handler
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
power: POWER, // <- resource declaration
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
use cortex_m::asm;
|
||||
use dk::peripheral::POWER;
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {
|
||||
power: POWER,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
|
||||
#[init]
|
||||
fn init(_cx: init::Context) -> init::LateResources {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
let power = board.power;
|
||||
|
||||
power.intenset.write(|w| w.usbdetected().set_bit());
|
||||
|
||||
log::info!("USBDETECTED interrupt enabled");
|
||||
defmt::println!("USBDETECTED interrupt enabled");
|
||||
|
||||
init::LateResources {
|
||||
power, // <- resource initialization
|
||||
}
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources { power },
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[idle]
|
||||
fn main(_cx: main::Context) -> ! {
|
||||
fn idle(_cx: idle::Context) -> ! {
|
||||
loop {
|
||||
log::info!("idle: going to sleep");
|
||||
defmt::println!("idle: going to sleep");
|
||||
asm::wfi();
|
||||
log::info!("idle: woke up");
|
||||
defmt::println!("idle: woke up");
|
||||
}
|
||||
}
|
||||
|
||||
#[task(binds = POWER_CLOCK, resources = [power])]
|
||||
#[task(binds = POWER_CLOCK, local = [power])]
|
||||
// ^^^^^^^ resource access list
|
||||
fn on_power_event(cx: on_power_event::Context) {
|
||||
log::info!("POWER event occurred");
|
||||
defmt::println!("POWER event occurred");
|
||||
|
||||
// resources available to this task
|
||||
let resources = cx.resources;
|
||||
let resources = cx.local;
|
||||
|
||||
// the POWER peripheral can be accessed through a reference
|
||||
let power: &mut POWER = resources.power;
|
||||
|
@ -49,4 +57,4 @@ const APP: () = {
|
|||
// clear the interrupt flag; otherwise this task will run again after it returns
|
||||
power.events_usbdetected.reset();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,24 +1,37 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use cortex_m::asm;
|
||||
use panic_log as _; // panic handler
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
use cortex_m::asm;
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
#[init]
|
||||
fn init(_cx: init::Context) {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
dk::init().unwrap();
|
||||
|
||||
log::info!("Hello");
|
||||
defmt::println!("Hello");
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources {},
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[idle]
|
||||
fn main(_cx: main::Context) -> ! {
|
||||
log::info!("world!");
|
||||
fn idle(_cx: idle::Context) -> ! {
|
||||
defmt::println!("world!");
|
||||
|
||||
loop {
|
||||
asm::bkpt();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,15 +3,15 @@
|
|||
|
||||
use cortex_m::asm;
|
||||
use cortex_m_rt::entry;
|
||||
use panic_log as _; // panic handler
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
// board initialization
|
||||
dk::init().unwrap();
|
||||
|
||||
log::info!("provoking stack overflow...");
|
||||
spam(0);
|
||||
fib(100);
|
||||
|
||||
loop {
|
||||
asm::bkpt();
|
||||
|
@ -19,16 +19,14 @@ fn main() -> ! {
|
|||
}
|
||||
|
||||
#[inline(never)]
|
||||
fn spam(n: u32) {
|
||||
// allocate and initialize 4 kilobytes of stack memory to provoke stack overflow
|
||||
let use_stack = [n; 1024];
|
||||
fn fib(n: u32) -> u32 {
|
||||
// allocate and initialize one kilobyte of stack memory to provoke stack overflow
|
||||
let use_stack = [0xAA; 1024];
|
||||
defmt::println!("allocating [{}; 1024]; round #{}", use_stack[1023], n);
|
||||
|
||||
log::info!(
|
||||
"address of current `use_stack` at recursion depth {:?}: {:?}",
|
||||
use_stack[1023], // "use" use_stack to prevent it from being optimized out
|
||||
&use_stack as *const u32
|
||||
);
|
||||
|
||||
let next = n + 1;
|
||||
spam(next); // infinite recursion
|
||||
if n < 2 {
|
||||
1
|
||||
} else {
|
||||
fib(n - 1) + fib(n - 2) // recursion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +1,66 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Event},
|
||||
};
|
||||
use panic_log as _; // panic handler
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
use cortex_m::asm;
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Event},
|
||||
};
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {
|
||||
usbd: USBD,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
|
||||
#[init]
|
||||
fn init(_cx: init::Context) -> init::LateResources {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
usbd::init(board.power, &board.usbd);
|
||||
// initialize the USBD peripheral
|
||||
// NOTE this will block if the USB cable is not connected to port J3
|
||||
dk::usbd::init(board.power, &board.usbd);
|
||||
|
||||
init::LateResources { usbd: board.usbd }
|
||||
defmt::println!("USBD initialized");
|
||||
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources { usbd: board.usbd },
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[task(binds = USBD, resources = [usbd])]
|
||||
#[task(binds = USBD, local = [usbd])]
|
||||
fn main(cx: main::Context) {
|
||||
let usbd = cx.resources.usbd;
|
||||
let usbd = cx.local.usbd;
|
||||
|
||||
while let Some(event) = usbd::next_event(usbd) {
|
||||
on_event(usbd, event)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn on_event(_usbd: &USBD, event: Event) {
|
||||
log::info!("USB: {:?}", event);
|
||||
fn on_event(_usbd: &USBD, event: Event) {
|
||||
defmt::println!("USB: {}", event);
|
||||
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
// going from the Default state to the Default state is a no-operation
|
||||
log::info!("returning to the Default state");
|
||||
}
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
// going from the Default state to the Default state is a no-operation
|
||||
defmt::println!("returning to the Default state");
|
||||
}
|
||||
|
||||
Event::UsbEp0DataDone => todo!(),
|
||||
Event::UsbEp0DataDone => todo!(),
|
||||
|
||||
Event::UsbEp0Setup => {
|
||||
log::info!("goal reached; move to the next section");
|
||||
dk::exit()
|
||||
Event::UsbEp0Setup => {
|
||||
defmt::println!("goal reached; move to the next section");
|
||||
dk::exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +1,63 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Event},
|
||||
};
|
||||
use panic_log as _; // panic handler
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
use cortex_m::asm;
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Event},
|
||||
};
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {
|
||||
usbd: USBD,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
|
||||
#[init]
|
||||
fn init(_cx: init::Context) -> init::LateResources {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
// initialize the USBD peripheral
|
||||
// NOTE this will block if the USB cable is not connected to port J3
|
||||
usbd::init(board.power, &board.usbd);
|
||||
|
||||
log::info!("USBD initialized");
|
||||
defmt::println!("USBD initialized");
|
||||
|
||||
init::LateResources { usbd: board.usbd }
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources { usbd: board.usbd },
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[task(binds = USBD, resources = [usbd])]
|
||||
#[task(binds = USBD, local = [usbd])]
|
||||
fn main(cx: main::Context) {
|
||||
let usbd = cx.resources.usbd;
|
||||
let usbd = cx.local.usbd;
|
||||
|
||||
while let Some(event) = usbd::next_event(usbd) {
|
||||
on_event(usbd, event)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn on_event(_usbd: &USBD, event: Event) {
|
||||
log::info!("USB: {:?} @ {:?}", event, dk::uptime());
|
||||
fn on_event(_usbd: &USBD, event: Event) {
|
||||
defmt::println!("USB: {} @ {}", event, dk::uptime());
|
||||
|
||||
match event {
|
||||
Event::UsbReset => todo!(),
|
||||
match event {
|
||||
Event::UsbReset => todo!(),
|
||||
|
||||
Event::UsbEp0DataDone => todo!(),
|
||||
// leave this at it is for now.
|
||||
Event::UsbEp0Setup => {
|
||||
log::info!("goal reached; move to the next section");
|
||||
dk::exit()
|
||||
Event::UsbEp0DataDone => todo!(),
|
||||
// leave this at it is for now.
|
||||
Event::UsbEp0Setup => {
|
||||
defmt::println!("goal reached; move to the next section");
|
||||
dk::exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,100 +1,111 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Event},
|
||||
};
|
||||
use panic_log as _; // panic handler
|
||||
use usb::{Descriptor, Request};
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Event},
|
||||
};
|
||||
|
||||
use usb::{Descriptor, Request};
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {
|
||||
usbd: USBD,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
|
||||
#[init]
|
||||
fn init(_cx: init::Context) -> init::LateResources {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
usbd::init(board.power, &board.usbd);
|
||||
|
||||
init::LateResources { usbd: board.usbd }
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources { usbd: board.usbd },
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[task(binds = USBD, resources = [usbd])]
|
||||
#[task(binds = USBD, local = [usbd])]
|
||||
fn main(cx: main::Context) {
|
||||
let usbd = cx.resources.usbd;
|
||||
let usbd = cx.local.usbd;
|
||||
|
||||
while let Some(event) = usbd::next_event(usbd) {
|
||||
on_event(usbd, event)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn on_event(usbd: &USBD, event: Event) {
|
||||
log::info!("USB: {:?} @ {:?}", event, dk::uptime());
|
||||
fn on_event(usbd: &USBD, event: Event) {
|
||||
defmt::println!("USB: {} @ {}", event, dk::uptime());
|
||||
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
// nothing to do here at the moment
|
||||
}
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
// nothing to do here at the moment
|
||||
}
|
||||
|
||||
Event::UsbEp0DataDone => todo!(),
|
||||
Event::UsbEp0DataDone => todo!(),
|
||||
|
||||
Event::UsbEp0Setup => {
|
||||
// the BMREQUESTTYPE register contains information about data recipient, transfer type and direction
|
||||
let bmrequesttype = usbd.bmrequesttype.read().bits() as u8;
|
||||
// the BREQUEST register stores the type of the current request (e.g. SET_ADDRESS, GET_DESCRIPTOR, ...)
|
||||
let brequest = usbd.brequest.read().brequest().bits();
|
||||
// wLength denotes the number of bytes to transfer (if any)
|
||||
// composed of a high register (WLENGTHH) and a low register (WLENGTHL)
|
||||
let wlength = (u16::from(usbd.wlengthh.read().wlengthh().bits()) << 8)
|
||||
| u16::from(usbd.wlengthl.read().wlengthl().bits());
|
||||
// wIndex is a generic index field whose meaning depends on the request type
|
||||
// composed of a high register (WINDEXH) and a low register (WINDEXL)
|
||||
let windex = (u16::from(usbd.windexh.read().windexh().bits()) << 8)
|
||||
| u16::from(usbd.windexl.read().windexl().bits());
|
||||
// wValue is a generic paremeter field meaning depends on the request type (e.g. contains the device
|
||||
// address in SET_ADRESS requests)
|
||||
// composed of a high register (WVALUEH) and a low register (WVALUEL)
|
||||
let wvalue = (u16::from(usbd.wvalueh.read().wvalueh().bits()) << 8)
|
||||
| u16::from(usbd.wvaluel.read().wvaluel().bits());
|
||||
Event::UsbEp0Setup => {
|
||||
// the BMREQUESTTYPE register contains information about data recipient, transfer type and direction
|
||||
let bmrequesttype = usbd.bmrequesttype.read().bits() as u8;
|
||||
// the BREQUEST register stores the type of the current request (e.g. SET_ADDRESS, GET_DESCRIPTOR, ...)
|
||||
let brequest = usbd.brequest.read().brequest().bits();
|
||||
// wLength denotes the number of bytes to transfer (if any)
|
||||
// composed of a high register (WLENGTHH) and a low register (WLENGTHL)
|
||||
let wlength = (u16::from(usbd.wlengthh.read().wlengthh().bits()) << 8)
|
||||
| u16::from(usbd.wlengthl.read().wlengthl().bits());
|
||||
// wIndex is a generic index field whose meaning depends on the request type
|
||||
// composed of a high register (WINDEXH) and a low register (WINDEXL)
|
||||
let windex = (u16::from(usbd.windexh.read().windexh().bits()) << 8)
|
||||
| u16::from(usbd.windexl.read().windexl().bits());
|
||||
// wValue is a generic paremeter field meaning depends on the request type (e.g. contains the device
|
||||
// address in SET_ADRESS requests)
|
||||
// composed of a high register (WVALUEH) and a low register (WVALUEL)
|
||||
let wvalue = (u16::from(usbd.wvalueh.read().wvalueh().bits()) << 8)
|
||||
| u16::from(usbd.wvaluel.read().wvaluel().bits());
|
||||
|
||||
// NOTE the `dk` crate contains helper functions for the above operations
|
||||
// let bmrequesttype = usbd::bmrequesttype(usbd);
|
||||
// let brequest = usbd::brequest(usbd);
|
||||
// let wlength = usbd::wlength(usbd);
|
||||
// let windex = usbd::windex(usbd);
|
||||
// let wvalue = usbd::wvalue(usbd);
|
||||
// NOTE the `dk` crate contains helper functions for the above operations
|
||||
// let bmrequesttype = usbd::bmrequesttype(usbd);
|
||||
// let brequest = usbd::brequest(usbd);
|
||||
// let wlength = usbd::wlength(usbd);
|
||||
// let windex = usbd::windex(usbd);
|
||||
// let wvalue = usbd::wvalue(usbd);
|
||||
|
||||
log::info!(
|
||||
"SETUP: bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
defmt::println!(
|
||||
"SETUP: bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
|
||||
.expect("Error parsing request");
|
||||
match request {
|
||||
Request::GetDescriptor { descriptor, length }
|
||||
if descriptor == Descriptor::Device =>
|
||||
{
|
||||
log::info!("GET_DESCRIPTOR Device [length={}]", length);
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
|
||||
.expect("Error parsing request");
|
||||
match request {
|
||||
Request::GetDescriptor { descriptor, length }
|
||||
if descriptor == Descriptor::Device =>
|
||||
{
|
||||
defmt::println!("GET_DESCRIPTOR Device [length={}]", length);
|
||||
|
||||
log::info!("Goal reached; move to the next section");
|
||||
dk::exit()
|
||||
defmt::println!("Goal reached; move to the next section");
|
||||
dk::exit()
|
||||
}
|
||||
Request::SetAddress { .. } => {
|
||||
// On Mac OS you'll get this request before the GET_DESCRIPTOR request so we
|
||||
// need to catch it here. We'll properly handle this request later
|
||||
// but for now it's OK to do nothing.
|
||||
}
|
||||
_ => unreachable!(), // we don't handle any other Requests
|
||||
}
|
||||
Request::SetAddress { .. } => {
|
||||
// On Mac OS you'll get this request before the GET_DESCRIPTOR request so we
|
||||
// need to catch it here. We'll properly handle this request later
|
||||
// but for now it's OK to do nothing.
|
||||
}
|
||||
_ => unreachable!(), // we don't handle any other Requests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,95 +1,106 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Event},
|
||||
};
|
||||
use panic_log as _; // panic handler
|
||||
use usb::{Descriptor, Request};
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Event},
|
||||
};
|
||||
|
||||
use usb::{Descriptor, Request};
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {
|
||||
usbd: USBD,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
|
||||
#[init]
|
||||
fn init(_cx: init::Context) -> init::LateResources {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
usbd::init(board.power, &board.usbd);
|
||||
|
||||
init::LateResources { usbd: board.usbd }
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources { usbd: board.usbd },
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[task(binds = USBD, resources = [usbd])]
|
||||
#[task(binds = USBD, local = [usbd])]
|
||||
fn main(cx: main::Context) {
|
||||
let usbd = cx.resources.usbd;
|
||||
let usbd = cx.local.usbd;
|
||||
|
||||
while let Some(event) = usbd::next_event(usbd) {
|
||||
on_event(usbd, event)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn on_event(_usbd: &USBD, event: Event) {
|
||||
log::info!("USB: {:?} @ {:?}", event, dk::uptime());
|
||||
fn on_event(_usbd: &USBD, event: Event) {
|
||||
defmt::println!("USB: {} @ {}", event, dk::uptime());
|
||||
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
// nothing to do here at the moment
|
||||
}
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
// nothing to do here at the moment
|
||||
}
|
||||
|
||||
Event::UsbEp0DataDone => todo!(),
|
||||
Event::UsbEp0DataDone => todo!(),
|
||||
|
||||
Event::UsbEp0Setup => {
|
||||
// TODO read USBD registers
|
||||
Event::UsbEp0Setup => {
|
||||
// TODO read USBD registers
|
||||
|
||||
// the BMREQUESTTYPE register contains information about data recipient, transfer type and direction
|
||||
let bmrequesttype: u8 = 0;
|
||||
// the BREQUEST register stores the type of the current request (e.g. SET_ADDRESS, GET_DESCRIPTOR, ...)
|
||||
let brequest: u8 = 0;
|
||||
// wLength denotes the number of bytes to transfer (if any)
|
||||
// composed of a high register (WLENGTHH) and a low register (WLENGTHL)
|
||||
let wlength: u16 = 0;
|
||||
// wIndex is a generic index field whose meaning depends on the request type
|
||||
// composed of a high register (WINDEXH) and a low register (WINDEXL)
|
||||
let windex: u16 = 0;
|
||||
// wValue is a generic paremeter field meaning depends on the request type (e.g. contains the device
|
||||
// address in SET_ADRESS requests)
|
||||
// composed of a high register (WVALUEH) and a low register (WVALUEL)
|
||||
let wvalue: u16 = 0;
|
||||
// the BMREQUESTTYPE register contains information about data recipient, transfer type and direction
|
||||
let bmrequesttype: u8 = 0;
|
||||
// the BREQUEST register stores the type of the current request (e.g. SET_ADDRESS, GET_DESCRIPTOR, ...)
|
||||
let brequest: u8 = 0;
|
||||
// wLength denotes the number of bytes to transfer (if any)
|
||||
// composed of a high register (WLENGTHH) and a low register (WLENGTHL)
|
||||
let wlength: u16 = 0;
|
||||
// wIndex is a generic index field whose meaning depends on the request type
|
||||
// composed of a high register (WINDEXH) and a low register (WINDEXL)
|
||||
let windex: u16 = 0;
|
||||
// wValue is a generic paremeter field meaning depends on the request type (e.g. contains the device
|
||||
// address in SET_ADRESS requests)
|
||||
// composed of a high register (WVALUEH) and a low register (WVALUEL)
|
||||
let wvalue: u16 = 0;
|
||||
|
||||
log::info!(
|
||||
"SETUP: bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
defmt::println!(
|
||||
"SETUP: bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
|
||||
.expect("Error parsing request");
|
||||
match request {
|
||||
Request::GetDescriptor { descriptor, length }
|
||||
if descriptor == Descriptor::Device =>
|
||||
{
|
||||
// TODO modify `Request::parse()` in `advanced/common/usb/lib.rs`
|
||||
// so that this branch is reached
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
|
||||
.expect("Error parsing request");
|
||||
match request {
|
||||
Request::GetDescriptor { descriptor, length }
|
||||
if descriptor == Descriptor::Device =>
|
||||
{
|
||||
// TODO modify `Request::parse()` in `advanced/common/usb/lib.rs`
|
||||
// so that this branch is reached
|
||||
|
||||
log::info!("GET_DESCRIPTOR Device [length={}]", length);
|
||||
defmt::println!("GET_DESCRIPTOR Device [length={}]", length);
|
||||
|
||||
log::info!("Goal reached; move to the next section");
|
||||
dk::exit()
|
||||
defmt::println!("Goal reached; move to the next section");
|
||||
dk::exit()
|
||||
}
|
||||
Request::SetAddress { .. } => {
|
||||
// On Mac OS you'll get this request before the GET_DESCRIPTOR request so we
|
||||
// need to catch it here. We'll properly handle this request later
|
||||
// but for now it's OK to do nothing.
|
||||
}
|
||||
_ => unreachable!(), // we don't handle any other Requests
|
||||
}
|
||||
Request::SetAddress { .. } => {
|
||||
// On Mac OS you'll get this request before the GET_DESCRIPTOR request so we
|
||||
// need to catch it here. We'll properly handle this request later
|
||||
// but for now it's OK to do nothing.
|
||||
}
|
||||
_ => unreachable!(), // we don't handle any other Requests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,105 +1,117 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Ep0In, Event},
|
||||
};
|
||||
use panic_log as _; // panic handler
|
||||
use usb::{Descriptor, Request};
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Ep0In, Event},
|
||||
};
|
||||
|
||||
use usb::{Descriptor, Request};
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {
|
||||
usbd: USBD,
|
||||
ep0in: Ep0In,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
|
||||
#[init]
|
||||
fn init(_cx: init::Context) -> init::LateResources {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
usbd::init(board.power, &board.usbd);
|
||||
|
||||
init::LateResources {
|
||||
ep0in: board.ep0in,
|
||||
usbd: board.usbd,
|
||||
}
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources {
|
||||
usbd: board.usbd,
|
||||
ep0in: board.ep0in,
|
||||
},
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[task(binds = USBD, resources = [usbd, ep0in])]
|
||||
#[task(binds = USBD, local = [usbd, ep0in])]
|
||||
fn main(cx: main::Context) {
|
||||
let usbd = cx.resources.usbd;
|
||||
let ep0in = cx.resources.ep0in;
|
||||
let usbd = cx.local.usbd;
|
||||
let ep0in = cx.local.ep0in;
|
||||
|
||||
while let Some(event) = usbd::next_event(usbd) {
|
||||
on_event(usbd, ep0in, event)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn on_event(usbd: &USBD, ep0in: &mut Ep0In, event: Event) {
|
||||
log::info!("USB: {:?} @ {:?}", event, dk::uptime());
|
||||
fn on_event(usbd: &USBD, ep0in: &mut Ep0In, event: Event) {
|
||||
defmt::println!("USB: {} @ {}", event, dk::uptime());
|
||||
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
// nothing to do here at the moment
|
||||
}
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
// nothing to do here at the moment
|
||||
}
|
||||
|
||||
Event::UsbEp0DataDone => ep0in.end(usbd),
|
||||
Event::UsbEp0DataDone => ep0in.end(usbd),
|
||||
|
||||
Event::UsbEp0Setup => {
|
||||
let bmrequesttype = usbd.bmrequesttype.read().bits() as u8;
|
||||
let brequest = usbd.brequest.read().brequest().bits();
|
||||
let wlength = usbd::wlength(usbd);
|
||||
let windex = usbd::windex(usbd);
|
||||
let wvalue = usbd::wvalue(usbd);
|
||||
Event::UsbEp0Setup => {
|
||||
let bmrequesttype = usbd.bmrequesttype.read().bits() as u8;
|
||||
let brequest = usbd.brequest.read().brequest().bits();
|
||||
let wlength = usbd::wlength(usbd);
|
||||
let windex = usbd::windex(usbd);
|
||||
let wvalue = usbd::wvalue(usbd);
|
||||
|
||||
log::info!(
|
||||
"SETUP: bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
defmt::println!(
|
||||
"SETUP: bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength).expect(
|
||||
"Error parsing request (goal achieved if GET_DESCRIPTOR Device was handled before)",
|
||||
);
|
||||
match request {
|
||||
Request::GetDescriptor { descriptor, length }
|
||||
if descriptor == Descriptor::Device =>
|
||||
{
|
||||
log::info!("GET_DESCRIPTOR Device [length={}]", length);
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength).expect(
|
||||
"Error parsing request (goal achieved if GET_DESCRIPTOR Device was handled before)",
|
||||
);
|
||||
match request {
|
||||
Request::GetDescriptor { descriptor, length }
|
||||
if descriptor == Descriptor::Device =>
|
||||
{
|
||||
defmt::println!("GET_DESCRIPTOR Device [length={}]", length);
|
||||
|
||||
let desc = usb2::device::Descriptor {
|
||||
bDeviceClass: 0,
|
||||
bDeviceProtocol: 0,
|
||||
bDeviceSubClass: 0,
|
||||
bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64,
|
||||
bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(),
|
||||
bcdDevice: 0x01_00, // 1.00
|
||||
iManufacturer: None,
|
||||
iProduct: None,
|
||||
iSerialNumber: None,
|
||||
idProduct: consts::PID,
|
||||
idVendor: consts::VID,
|
||||
};
|
||||
let desc_bytes = desc.bytes();
|
||||
let resp = &desc_bytes[..core::cmp::min(desc_bytes.len(), usize::from(length))];
|
||||
ep0in.start(&resp, usbd);
|
||||
}
|
||||
Request::SetAddress { .. } => {
|
||||
// On Mac OS you'll get this request before the GET_DESCRIPTOR request so we
|
||||
// need to catch it here. We'll properly handle this request later
|
||||
// but for now it's OK to do nothing.
|
||||
}
|
||||
_ => {
|
||||
log::error!(
|
||||
"unknown request (goal achieved if GET_DESCRIPTOR Device was handled before)"
|
||||
);
|
||||
dk::exit()
|
||||
let desc = usb2::device::Descriptor {
|
||||
bDeviceClass: 0,
|
||||
bDeviceProtocol: 0,
|
||||
bDeviceSubClass: 0,
|
||||
bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64,
|
||||
bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(),
|
||||
bcdDevice: 0x01_00, // 1.00
|
||||
iManufacturer: None,
|
||||
iProduct: None,
|
||||
iSerialNumber: None,
|
||||
idProduct: consts::PID,
|
||||
idVendor: consts::VID,
|
||||
};
|
||||
let desc_bytes = desc.bytes();
|
||||
let resp =
|
||||
&desc_bytes[..core::cmp::min(desc_bytes.len(), usize::from(length))];
|
||||
ep0in.start(&resp, usbd);
|
||||
}
|
||||
Request::SetAddress { .. } => {
|
||||
// On Mac OS you'll get this request before the GET_DESCRIPTOR request so we
|
||||
// need to catch it here. We'll properly handle this request later
|
||||
// but for now it's OK to do nothing.
|
||||
}
|
||||
_ => {
|
||||
defmt::error!(
|
||||
"unknown request (goal achieved if GET_DESCRIPTOR Device was handled before)"
|
||||
);
|
||||
dk::exit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,96 +1,102 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Ep0In, Event},
|
||||
};
|
||||
use panic_log as _; // panic handler
|
||||
use usb::{Descriptor, Request};
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Ep0In, Event},
|
||||
};
|
||||
use usb::{Descriptor, Request};
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {
|
||||
usbd: USBD,
|
||||
ep0in: Ep0In,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
#[init]
|
||||
fn init(_cx: init::Context) -> init::LateResources {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
usbd::init(board.power, &board.usbd);
|
||||
|
||||
init::LateResources {
|
||||
ep0in: board.ep0in,
|
||||
usbd: board.usbd,
|
||||
}
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources {
|
||||
usbd: board.usbd,
|
||||
ep0in: board.ep0in,
|
||||
},
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[task(binds = USBD, resources = [usbd, ep0in])]
|
||||
#[task(binds = USBD, local = [usbd, ep0in])]
|
||||
fn main(cx: main::Context) {
|
||||
let usbd = cx.resources.usbd;
|
||||
let ep0in = cx.resources.ep0in;
|
||||
let usbd = cx.local.usbd;
|
||||
let ep0in = cx.local.ep0in;
|
||||
|
||||
while let Some(event) = usbd::next_event(usbd) {
|
||||
on_event(usbd, ep0in, event)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn on_event(usbd: &USBD, ep0in: &mut Ep0In, event: Event) {
|
||||
log::info!("USB: {:?} @ {:?}", event, dk::uptime());
|
||||
fn on_event(usbd: &USBD, ep0in: &mut Ep0In, event: Event) {
|
||||
defmt::println!("USB: {} @ {}", event, dk::uptime());
|
||||
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
// nothing to do here at the moment
|
||||
}
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
// nothing to do here at the moment
|
||||
}
|
||||
|
||||
Event::UsbEp0DataDone => todo!(), // <- TODO
|
||||
Event::UsbEp0DataDone => todo!(), // <- TODO
|
||||
|
||||
Event::UsbEp0Setup => {
|
||||
let bmrequesttype = usbd::bmrequesttype(usbd);
|
||||
let brequest = usbd::brequest(usbd);
|
||||
let wlength = usbd::wlength(usbd);
|
||||
let windex = usbd::windex(usbd);
|
||||
let wvalue = usbd::wvalue(usbd);
|
||||
Event::UsbEp0Setup => {
|
||||
let bmrequesttype = usbd::bmrequesttype(usbd);
|
||||
let brequest = usbd::brequest(usbd);
|
||||
let wlength = usbd::wlength(usbd);
|
||||
let windex = usbd::windex(usbd);
|
||||
let wvalue = usbd::wvalue(usbd);
|
||||
|
||||
log::info!(
|
||||
"SETUP: bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
defmt::println!(
|
||||
"SETUP: bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength).expect(
|
||||
"Error parsing request (goal achieved if GET_DESCRIPTOR Device was handled before)",
|
||||
);
|
||||
match request {
|
||||
Request::GetDescriptor { descriptor, length }
|
||||
if descriptor == Descriptor::Device =>
|
||||
{
|
||||
log::info!("GET_DESCRIPTOR Device [length={}]", length);
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength).expect(
|
||||
"Error parsing request (goal achieved if GET_DESCRIPTOR Device was handled before)",
|
||||
);
|
||||
match request {
|
||||
Request::GetDescriptor { descriptor, length }
|
||||
if descriptor == Descriptor::Device =>
|
||||
{
|
||||
defmt::println!("GET_DESCRIPTOR Device [length={}]", length);
|
||||
|
||||
// TODO send back a valid device descriptor, truncated to `length` bytes
|
||||
// let desc = usb2::device::Descriptor { .. };
|
||||
let resp = [];
|
||||
|
||||
// ensure we're not overstepping boundaries
|
||||
assert!(resp.len() <= length as usize);
|
||||
ep0in.start(&resp, usbd);
|
||||
}
|
||||
Request::SetAddress { .. } => {
|
||||
// On Mac OS you'll get this request before the GET_DESCRIPTOR request so we
|
||||
// need to catch it here. We'll properly handle this request later
|
||||
// but for now it's OK to do nothing.
|
||||
}
|
||||
_ => {
|
||||
log::error!(
|
||||
"unknown request (goal achieved if GET_DESCRIPTOR Device was handled before)"
|
||||
);
|
||||
dk::exit()
|
||||
// TODO send back a valid device descriptor, truncated to `length` bytes
|
||||
// let desc = usb2::device::Descriptor { .. };
|
||||
let resp = [];
|
||||
ep0in.start(&resp, usbd);
|
||||
}
|
||||
Request::SetAddress { .. } => {
|
||||
// On Mac OS you'll get this request before the GET_DESCRIPTOR request so we
|
||||
// need to catch it here. We'll properly handle this request later
|
||||
// but for now it's OK to do nothing.
|
||||
}
|
||||
_ => {
|
||||
defmt::error!(
|
||||
"unknown request (goal achieved if GET_DESCRIPTOR Device was handled before)"
|
||||
);
|
||||
dk::exit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,185 +1,195 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::num::NonZeroU8;
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Ep0In, Event},
|
||||
};
|
||||
use panic_log as _; // panic handler
|
||||
use usb2::{GetDescriptor as Descriptor, StandardRequest as Request, State};
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
use core::num::NonZeroU8;
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Ep0In, Event},
|
||||
};
|
||||
use usb2::{GetDescriptor as Descriptor, StandardRequest as Request, State};
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {
|
||||
usbd: USBD,
|
||||
ep0in: Ep0In,
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
#[init]
|
||||
fn init(_cx: init::Context) -> init::LateResources {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
usbd::init(board.power, &board.usbd);
|
||||
|
||||
init::LateResources {
|
||||
usbd: board.usbd,
|
||||
state: State::Default,
|
||||
ep0in: board.ep0in,
|
||||
}
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources {
|
||||
usbd: board.usbd,
|
||||
ep0in: board.ep0in,
|
||||
state: State::Default,
|
||||
},
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[task(binds = USBD, resources = [usbd, ep0in, state])]
|
||||
#[task(binds = USBD, local = [usbd, ep0in, state])]
|
||||
fn main(cx: main::Context) {
|
||||
let usbd = cx.resources.usbd;
|
||||
let ep0in = cx.resources.ep0in;
|
||||
let state = cx.resources.state;
|
||||
let usbd = cx.local.usbd;
|
||||
let ep0in = cx.local.ep0in;
|
||||
let state = cx.local.state;
|
||||
|
||||
while let Some(event) = usbd::next_event(usbd) {
|
||||
on_event(usbd, ep0in, state, event)
|
||||
}
|
||||
}
|
||||
};
|
||||
fn on_event(usbd: &USBD, ep0in: &mut Ep0In, state: &mut State, event: Event) {
|
||||
defmt::println!("USB: {} @ {}", event, dk::uptime());
|
||||
|
||||
fn on_event(usbd: &USBD, ep0in: &mut Ep0In, state: &mut State, event: Event) {
|
||||
log::info!("USB: {:?} @ {:?}", event, dk::uptime());
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
defmt::println!("USB reset condition detected");
|
||||
*state = State::Default;
|
||||
}
|
||||
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
log::info!("USB reset condition detected");
|
||||
*state = State::Default;
|
||||
}
|
||||
Event::UsbEp0DataDone => {
|
||||
defmt::println!("EP0IN: transfer complete");
|
||||
ep0in.end(usbd);
|
||||
}
|
||||
|
||||
Event::UsbEp0DataDone => {
|
||||
log::info!("EP0IN: transfer complete");
|
||||
ep0in.end(usbd);
|
||||
}
|
||||
|
||||
Event::UsbEp0Setup => {
|
||||
if ep0setup(usbd, ep0in, state).is_err() {
|
||||
log::warn!("EP0IN: unexpected request; stalling the endpoint");
|
||||
usbd::ep0stall(usbd);
|
||||
Event::UsbEp0Setup => {
|
||||
if ep0setup(usbd, ep0in, state).is_err() {
|
||||
defmt::warn!("EP0IN: unexpected request; stalling the endpoint");
|
||||
usbd::ep0stall(usbd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The `bConfigurationValue` of the only supported configuration
|
||||
const CONFIG_VAL: u8 = 42;
|
||||
/// The `bConfigurationValue` of the only supported configuration
|
||||
const CONFIG_VAL: u8 = 42;
|
||||
|
||||
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);
|
||||
let windex = usbd::windex(usbd);
|
||||
let wvalue = usbd::wvalue(usbd);
|
||||
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);
|
||||
let windex = usbd::windex(usbd);
|
||||
let wvalue = usbd::wvalue(usbd);
|
||||
|
||||
log::info!(
|
||||
"bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
defmt::println!(
|
||||
"bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
|
||||
.expect("Error parsing request");
|
||||
log::info!("EP0: {:?}", request);
|
||||
|
||||
match request {
|
||||
// section 9.4.3
|
||||
// this request is valid in any state
|
||||
Request::GetDescriptor { descriptor, length } => match descriptor {
|
||||
Descriptor::Device => {
|
||||
let desc = usb2::device::Descriptor {
|
||||
bDeviceClass: 0,
|
||||
bDeviceProtocol: 0,
|
||||
bDeviceSubClass: 0,
|
||||
bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64,
|
||||
bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(),
|
||||
bcdDevice: 0x01_00, // 1.00
|
||||
iManufacturer: None,
|
||||
iProduct: None,
|
||||
iSerialNumber: None,
|
||||
idProduct: consts::PID,
|
||||
idVendor: consts::VID,
|
||||
};
|
||||
let bytes = desc.bytes();
|
||||
let _ = ep0in.start(&bytes[..core::cmp::min(bytes.len(), length.into())], usbd);
|
||||
}
|
||||
|
||||
Descriptor::Configuration { index } => {
|
||||
if index == 0 {
|
||||
let mut resp = heapless::Vec::<u8, heapless::consts::U64>::new();
|
||||
|
||||
let conf_desc = usb2::configuration::Descriptor {
|
||||
wTotalLength: (usb2::configuration::Descriptor::SIZE
|
||||
+ usb2::interface::Descriptor::SIZE)
|
||||
.into(),
|
||||
bNumInterfaces: NonZeroU8::new(1).unwrap(),
|
||||
bConfigurationValue: core::num::NonZeroU8::new(CONFIG_VAL).unwrap(),
|
||||
iConfiguration: None,
|
||||
bmAttributes: usb2::configuration::bmAttributes {
|
||||
self_powered: true,
|
||||
remote_wakeup: false,
|
||||
},
|
||||
bMaxPower: 250, // 500 mA
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
|
||||
.expect("Error parsing request");
|
||||
defmt::println!("EP0: {}", defmt::Debug2Format(&request));
|
||||
// ^^^^^^^^^^^^^^^^^^^ this adapter is currently needed to log
|
||||
// `StandardRequest` with `defmt`
|
||||
match request {
|
||||
// section 9.4.3
|
||||
// this request is valid in any state
|
||||
Request::GetDescriptor { descriptor, length } => match descriptor {
|
||||
Descriptor::Device => {
|
||||
let desc = usb2::device::Descriptor {
|
||||
bDeviceClass: 0,
|
||||
bDeviceProtocol: 0,
|
||||
bDeviceSubClass: 0,
|
||||
bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64,
|
||||
bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(),
|
||||
bcdDevice: 0x01_00, // 1.00
|
||||
iManufacturer: None,
|
||||
iProduct: None,
|
||||
iSerialNumber: None,
|
||||
idProduct: consts::PID,
|
||||
idVendor: consts::VID,
|
||||
};
|
||||
|
||||
let iface_desc = usb2::interface::Descriptor {
|
||||
bInterfaceNumber: 0,
|
||||
bAlternativeSetting: 0,
|
||||
bNumEndpoints: 0,
|
||||
bInterfaceClass: 0,
|
||||
bInterfaceSubClass: 0,
|
||||
bInterfaceProtocol: 0,
|
||||
iInterface: None,
|
||||
};
|
||||
|
||||
resp.extend_from_slice(&conf_desc.bytes()).unwrap();
|
||||
resp.extend_from_slice(&iface_desc.bytes()).unwrap();
|
||||
ep0in.start(&resp[..core::cmp::min(resp.len(), length.into())], usbd);
|
||||
} else {
|
||||
// out of bounds access: stall the endpoint
|
||||
return Err(());
|
||||
let bytes = desc.bytes();
|
||||
let _ = ep0in.start(&bytes[..core::cmp::min(bytes.len(), length.into())], usbd);
|
||||
}
|
||||
|
||||
Descriptor::Configuration { index } => {
|
||||
if index == 0 {
|
||||
let mut resp = heapless::Vec::<u8, 64>::new();
|
||||
|
||||
let conf_desc = usb2::configuration::Descriptor {
|
||||
wTotalLength: (usb2::configuration::Descriptor::SIZE
|
||||
+ usb2::interface::Descriptor::SIZE)
|
||||
.into(),
|
||||
bNumInterfaces: NonZeroU8::new(1).unwrap(),
|
||||
bConfigurationValue: core::num::NonZeroU8::new(CONFIG_VAL).unwrap(),
|
||||
iConfiguration: None,
|
||||
bmAttributes: usb2::configuration::bmAttributes {
|
||||
self_powered: true,
|
||||
remote_wakeup: false,
|
||||
},
|
||||
bMaxPower: 250, // 500 mA
|
||||
};
|
||||
|
||||
let iface_desc = usb2::interface::Descriptor {
|
||||
bInterfaceNumber: 0,
|
||||
bAlternativeSetting: 0,
|
||||
bNumEndpoints: 0,
|
||||
bInterfaceClass: 0,
|
||||
bInterfaceSubClass: 0,
|
||||
bInterfaceProtocol: 0,
|
||||
iInterface: None,
|
||||
};
|
||||
|
||||
resp.extend_from_slice(&conf_desc.bytes()).unwrap();
|
||||
resp.extend_from_slice(&iface_desc.bytes()).unwrap();
|
||||
ep0in.start(&resp[..core::cmp::min(resp.len(), length.into())], usbd);
|
||||
} else {
|
||||
// out of bounds access: stall the endpoint
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
_ => return Err(()),
|
||||
},
|
||||
|
||||
Request::SetAddress { address } => {
|
||||
match state {
|
||||
State::Default => {
|
||||
if let Some(address) = address {
|
||||
*state = State::Address(address);
|
||||
} else {
|
||||
// stay in the default state
|
||||
}
|
||||
}
|
||||
|
||||
State::Address(..) => {
|
||||
if let Some(address) = address {
|
||||
// use the new address
|
||||
*state = State::Address(address);
|
||||
} else {
|
||||
*state = State::Default;
|
||||
}
|
||||
}
|
||||
|
||||
// unspecified behavior
|
||||
State::Configured { .. } => return Err(()),
|
||||
}
|
||||
|
||||
// the response to this request is handled in hardware
|
||||
}
|
||||
|
||||
// stall any other request
|
||||
_ => return Err(()),
|
||||
},
|
||||
|
||||
Request::SetAddress { address } => {
|
||||
match state {
|
||||
State::Default => {
|
||||
if let Some(address) = address {
|
||||
*state = State::Address(address);
|
||||
} else {
|
||||
// stay in the default state
|
||||
}
|
||||
}
|
||||
|
||||
State::Address(..) => {
|
||||
if let Some(address) = address {
|
||||
// use the new address
|
||||
*state = State::Address(address);
|
||||
} else {
|
||||
*state = State::Default;
|
||||
}
|
||||
}
|
||||
|
||||
// unspecified behavior
|
||||
State::Configured { .. } => return Err(()),
|
||||
}
|
||||
|
||||
// the response to this request is handled in hardware
|
||||
}
|
||||
|
||||
// stall any other request
|
||||
_ => return Err(()),
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,132 +1,146 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Ep0In, Event},
|
||||
};
|
||||
use panic_log as _; // panic handler
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
use usb2::State;
|
||||
// HEADS UP to use *your* USB packet parser uncomment line 12 and remove line 13
|
||||
// use usb::{Request, Descriptor};
|
||||
use usb2::{GetDescriptor as Descriptor, StandardRequest as Request};
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
use core::num::NonZeroU8;
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Ep0In, Event},
|
||||
};
|
||||
use usb2::State;
|
||||
// HEADS UP to use *your* USB packet parser uncomment line 12 and remove line 13
|
||||
// use usb::{Request, Descriptor};
|
||||
use usb2::{GetDescriptor as Descriptor, StandardRequest as Request};
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {
|
||||
usbd: USBD,
|
||||
ep0in: Ep0In,
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
|
||||
#[init]
|
||||
fn init(_cx: init::Context) -> init::LateResources {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
usbd::init(board.power, &board.usbd);
|
||||
|
||||
init::LateResources {
|
||||
usbd: board.usbd,
|
||||
state: State::Default,
|
||||
ep0in: board.ep0in,
|
||||
}
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources {
|
||||
usbd: board.usbd,
|
||||
ep0in: board.ep0in,
|
||||
state: State::Default,
|
||||
},
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[task(binds = USBD, resources = [usbd, ep0in, state])]
|
||||
#[task(binds = USBD, local = [usbd, ep0in, state])]
|
||||
fn main(cx: main::Context) {
|
||||
let usbd = cx.resources.usbd;
|
||||
let ep0in = cx.resources.ep0in;
|
||||
let state = cx.resources.state;
|
||||
let usbd = cx.local.usbd;
|
||||
let ep0in = cx.local.ep0in;
|
||||
let state = cx.local.state;
|
||||
|
||||
while let Some(event) = usbd::next_event(usbd) {
|
||||
on_event(usbd, ep0in, state, event)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fn on_event(usbd: &USBD, ep0in: &mut Ep0In, state: &mut State, event: Event) {
|
||||
log::info!("USB: {:?} @ {:?}", event, dk::uptime());
|
||||
fn on_event(usbd: &USBD, ep0in: &mut Ep0In, state: &mut State, event: Event) {
|
||||
defmt::println!("USB: {} @ {}", event, dk::uptime());
|
||||
|
||||
match event {
|
||||
// TODO change `state` as specified in chapter 9.1 USB Device States, of the USB specification
|
||||
Event::UsbReset => {
|
||||
log::info!("USB reset condition detected");
|
||||
todo!();
|
||||
}
|
||||
match event {
|
||||
// TODO change `state` as specified in chapter 9.1 USB Device States, of the USB specification
|
||||
Event::UsbReset => {
|
||||
defmt::println!("USB reset condition detected");
|
||||
todo!();
|
||||
}
|
||||
|
||||
Event::UsbEp0DataDone => {
|
||||
log::info!("EP0IN: transfer complete");
|
||||
ep0in.end(usbd);
|
||||
}
|
||||
Event::UsbEp0DataDone => {
|
||||
defmt::println!("EP0IN: transfer complete");
|
||||
ep0in.end(usbd);
|
||||
}
|
||||
|
||||
Event::UsbEp0Setup => {
|
||||
if ep0setup(usbd, ep0in, state).is_err() {
|
||||
// unsupported or invalid request:
|
||||
// TODO: add code to stall the endpoint
|
||||
log::warn!("EP0IN: unexpected request; stalling the endpoint");
|
||||
Event::UsbEp0Setup => {
|
||||
if ep0setup(usbd, ep0in, state).is_err() {
|
||||
// unsupported or invalid request:
|
||||
// TODO: add code to stall the endpoint
|
||||
defmt::warn!("EP0IN: unexpected request; stalling the endpoint");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
let windex = usbd::windex(usbd);
|
||||
let wvalue = usbd::wvalue(usbd);
|
||||
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);
|
||||
let windex = usbd::windex(usbd);
|
||||
let wvalue = usbd::wvalue(usbd);
|
||||
|
||||
log::info!(
|
||||
"bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
defmt::println!(
|
||||
"bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
|
||||
.expect("Error parsing request");
|
||||
log::info!("EP0: {:?}", request);
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
|
||||
.expect("Error parsing request");
|
||||
defmt::println!("EP0: {}", defmt::Debug2Format(&request));
|
||||
// ^^^^^^^^^^^^^^^^^^^ this adapter is currently needed to log
|
||||
// `StandardRequest` with `defmt`
|
||||
|
||||
match request {
|
||||
Request::GetDescriptor { descriptor, length } => match descriptor {
|
||||
Descriptor::Device => {
|
||||
let desc = usb2::device::Descriptor {
|
||||
bDeviceClass: 0,
|
||||
bDeviceProtocol: 0,
|
||||
bDeviceSubClass: 0,
|
||||
bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64,
|
||||
bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(),
|
||||
bcdDevice: 0x01_00, // 1.00
|
||||
iManufacturer: None,
|
||||
iProduct: None,
|
||||
iSerialNumber: None,
|
||||
idProduct: consts::PID,
|
||||
idVendor: consts::VID,
|
||||
};
|
||||
let bytes = desc.bytes();
|
||||
let _ = ep0in.start(&bytes[..core::cmp::min(bytes.len(), length.into())], usbd);
|
||||
match request {
|
||||
Request::GetDescriptor { descriptor, length } => match descriptor {
|
||||
Descriptor::Device => {
|
||||
let desc = usb2::device::Descriptor {
|
||||
bDeviceClass: 0,
|
||||
bDeviceProtocol: 0,
|
||||
bDeviceSubClass: 0,
|
||||
bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64,
|
||||
bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(),
|
||||
bcdDevice: 0x01_00, // 1.00
|
||||
iManufacturer: None,
|
||||
iProduct: None,
|
||||
iSerialNumber: None,
|
||||
idProduct: consts::PID,
|
||||
idVendor: consts::VID,
|
||||
};
|
||||
let bytes = desc.bytes();
|
||||
let _ = ep0in.start(&bytes[..core::cmp::min(bytes.len(), length.into())], usbd);
|
||||
}
|
||||
|
||||
// TODO implement Configuration descriptor
|
||||
// Descriptor::Configuration { .. } => todo!(),
|
||||
|
||||
// stall any other request
|
||||
_ => return Err(()),
|
||||
},
|
||||
Request::SetAddress { .. } => {
|
||||
// On Mac OS you'll get this request before the GET_DESCRIPTOR request so we
|
||||
// need to catch it here.
|
||||
|
||||
// TODO: handle this request properly now.
|
||||
todo!()
|
||||
}
|
||||
|
||||
// TODO implement Configuration descriptor
|
||||
// Descriptor::Configuration { .. } => todo!(),
|
||||
|
||||
// stall any other request
|
||||
_ => return Err(()),
|
||||
},
|
||||
Request::SetAddress { .. } => {
|
||||
// On Mac OS you'll get this request before the GET_DESCRIPTOR request so we
|
||||
// need to catch it here.
|
||||
|
||||
// TODO: handle this request properly now.
|
||||
todo!()
|
||||
}
|
||||
|
||||
// stall any other request
|
||||
_ => return Err(()),
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,232 +1,243 @@
|
|||
#![no_main]
|
||||
#![no_std]
|
||||
|
||||
use core::num::NonZeroU8;
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Ep0In, Event},
|
||||
};
|
||||
use panic_log as _; // panic handler
|
||||
use usb2::{GetDescriptor as Descriptor, StandardRequest as Request, State};
|
||||
#[rtic::app(device = dk, peripherals = false)]
|
||||
mod app {
|
||||
|
||||
#[rtic::app(device = dk)]
|
||||
const APP: () = {
|
||||
struct Resources {
|
||||
use core::num::NonZeroU8;
|
||||
|
||||
use dk::{
|
||||
peripheral::USBD,
|
||||
usbd::{self, Ep0In, Event},
|
||||
};
|
||||
use usb2::{GetDescriptor as Descriptor, StandardRequest as Request, State};
|
||||
|
||||
#[local]
|
||||
struct MyLocalResources {
|
||||
usbd: USBD,
|
||||
ep0in: Ep0In,
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[shared]
|
||||
struct MySharedResources {}
|
||||
#[init]
|
||||
fn init(_cx: init::Context) -> init::LateResources {
|
||||
fn init(_cx: init::Context) -> (MySharedResources, MyLocalResources, init::Monotonics) {
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
usbd::init(board.power, &board.usbd);
|
||||
|
||||
init::LateResources {
|
||||
usbd: board.usbd,
|
||||
state: State::Default,
|
||||
ep0in: board.ep0in,
|
||||
}
|
||||
(
|
||||
MySharedResources {},
|
||||
MyLocalResources {
|
||||
usbd: board.usbd,
|
||||
ep0in: board.ep0in,
|
||||
state: State::Default,
|
||||
},
|
||||
init::Monotonics(),
|
||||
)
|
||||
}
|
||||
|
||||
#[task(binds = USBD, resources = [usbd, ep0in, state])]
|
||||
#[task(binds = USBD, local = [usbd, ep0in, state])]
|
||||
fn main(cx: main::Context) {
|
||||
let usbd = cx.resources.usbd;
|
||||
let ep0in = cx.resources.ep0in;
|
||||
let state = cx.resources.state;
|
||||
let usbd = cx.local.usbd;
|
||||
let ep0in = cx.local.ep0in;
|
||||
let state = cx.local.state;
|
||||
|
||||
while let Some(event) = usbd::next_event(usbd) {
|
||||
on_event(usbd, ep0in, state, event)
|
||||
}
|
||||
}
|
||||
};
|
||||
fn on_event(usbd: &USBD, ep0in: &mut Ep0In, state: &mut State, event: Event) {
|
||||
defmt::println!("USB: {} @ {}", event, dk::uptime());
|
||||
|
||||
fn on_event(usbd: &USBD, ep0in: &mut Ep0In, state: &mut State, event: Event) {
|
||||
log::info!("USB: {:?} @ {:?}", event, dk::uptime());
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
defmt::println!("USB reset condition detected");
|
||||
*state = State::Default;
|
||||
}
|
||||
|
||||
match event {
|
||||
Event::UsbReset => {
|
||||
log::info!("USB reset condition detected");
|
||||
*state = State::Default;
|
||||
}
|
||||
Event::UsbEp0DataDone => {
|
||||
defmt::println!("EP0IN: transfer complete");
|
||||
ep0in.end(usbd);
|
||||
}
|
||||
|
||||
Event::UsbEp0DataDone => {
|
||||
log::info!("EP0IN: transfer complete");
|
||||
ep0in.end(usbd);
|
||||
}
|
||||
|
||||
Event::UsbEp0Setup => {
|
||||
if ep0setup(usbd, ep0in, state).is_err() {
|
||||
log::warn!("EP0IN: unexpected request; stalling the endpoint");
|
||||
usbd::ep0stall(usbd);
|
||||
Event::UsbEp0Setup => {
|
||||
if ep0setup(usbd, ep0in, state).is_err() {
|
||||
defmt::warn!("EP0IN: unexpected request; stalling the endpoint");
|
||||
usbd::ep0stall(usbd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The `bConfigurationValue` of the only supported configuration
|
||||
const CONFIG_VAL: u8 = 42;
|
||||
/// The `bConfigurationValue` of the only supported configuration
|
||||
const CONFIG_VAL: u8 = 42;
|
||||
|
||||
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);
|
||||
let windex = usbd::windex(usbd);
|
||||
let wvalue = usbd::wvalue(usbd);
|
||||
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);
|
||||
let windex = usbd::windex(usbd);
|
||||
let wvalue = usbd::wvalue(usbd);
|
||||
|
||||
log::info!(
|
||||
"bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
defmt::println!(
|
||||
"bmrequesttype: {}, brequest: {}, wlength: {}, windex: {}, wvalue: {}",
|
||||
bmrequesttype,
|
||||
brequest,
|
||||
wlength,
|
||||
windex,
|
||||
wvalue
|
||||
);
|
||||
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
|
||||
.expect("Error parsing request");
|
||||
log::info!("EP0: {:?}", request);
|
||||
let request = Request::parse(bmrequesttype, brequest, wvalue, windex, wlength)
|
||||
.expect("Error parsing request");
|
||||
defmt::println!("EP0: {}", defmt::Debug2Format(&request));
|
||||
// ^^^^^^^^^^^^^^^^^^^ this adapter is currently needed to log
|
||||
// `StandardRequest` with `defmt`
|
||||
|
||||
match request {
|
||||
// section 9.4.3
|
||||
// this request is valid in any state
|
||||
Request::GetDescriptor { descriptor, length } => match descriptor {
|
||||
Descriptor::Device => {
|
||||
let desc = usb2::device::Descriptor {
|
||||
bDeviceClass: 0,
|
||||
bDeviceProtocol: 0,
|
||||
bDeviceSubClass: 0,
|
||||
bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64,
|
||||
bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(),
|
||||
bcdDevice: 0x01_00, // 1.00
|
||||
iManufacturer: None,
|
||||
iProduct: None,
|
||||
iSerialNumber: None,
|
||||
idProduct: consts::PID,
|
||||
idVendor: consts::VID,
|
||||
};
|
||||
let bytes = desc.bytes();
|
||||
let _ = ep0in.start(&bytes[..core::cmp::min(bytes.len(), length.into())], usbd);
|
||||
}
|
||||
|
||||
Descriptor::Configuration { index } => {
|
||||
if index == 0 {
|
||||
let mut resp = heapless::Vec::<u8, heapless::consts::U64>::new();
|
||||
|
||||
let conf_desc = usb2::configuration::Descriptor {
|
||||
wTotalLength: (usb2::configuration::Descriptor::SIZE
|
||||
+ usb2::interface::Descriptor::SIZE)
|
||||
.into(),
|
||||
bNumInterfaces: NonZeroU8::new(1).unwrap(),
|
||||
bConfigurationValue: core::num::NonZeroU8::new(CONFIG_VAL).unwrap(),
|
||||
iConfiguration: None,
|
||||
bmAttributes: usb2::configuration::bmAttributes {
|
||||
self_powered: true,
|
||||
remote_wakeup: false,
|
||||
},
|
||||
bMaxPower: 250, // 500 mA
|
||||
match request {
|
||||
// section 9.4.3
|
||||
// this request is valid in any state
|
||||
Request::GetDescriptor { descriptor, length } => match descriptor {
|
||||
Descriptor::Device => {
|
||||
let desc = usb2::device::Descriptor {
|
||||
bDeviceClass: 0,
|
||||
bDeviceProtocol: 0,
|
||||
bDeviceSubClass: 0,
|
||||
bMaxPacketSize0: usb2::device::bMaxPacketSize0::B64,
|
||||
bNumConfigurations: core::num::NonZeroU8::new(1).unwrap(),
|
||||
bcdDevice: 0x01_00, // 1.00
|
||||
iManufacturer: None,
|
||||
iProduct: None,
|
||||
iSerialNumber: None,
|
||||
idProduct: consts::PID,
|
||||
idVendor: consts::VID,
|
||||
};
|
||||
|
||||
let iface_desc = usb2::interface::Descriptor {
|
||||
bInterfaceNumber: 0,
|
||||
bAlternativeSetting: 0,
|
||||
bNumEndpoints: 0,
|
||||
bInterfaceClass: 0,
|
||||
bInterfaceSubClass: 0,
|
||||
bInterfaceProtocol: 0,
|
||||
iInterface: None,
|
||||
};
|
||||
|
||||
resp.extend_from_slice(&conf_desc.bytes()).unwrap();
|
||||
resp.extend_from_slice(&iface_desc.bytes()).unwrap();
|
||||
ep0in.start(&resp[..core::cmp::min(resp.len(), length.into())], usbd);
|
||||
} else {
|
||||
// out of bounds access: stall the endpoint
|
||||
return Err(());
|
||||
let bytes = desc.bytes();
|
||||
let _ = ep0in.start(&bytes[..core::cmp::min(bytes.len(), length.into())], usbd);
|
||||
}
|
||||
}
|
||||
|
||||
_ => return Err(()),
|
||||
},
|
||||
Descriptor::Configuration { index } => {
|
||||
if index == 0 {
|
||||
let mut resp = heapless::Vec::<u8, 64>::new();
|
||||
|
||||
Request::SetAddress { address } => {
|
||||
match state {
|
||||
State::Default => {
|
||||
if let Some(address) = address {
|
||||
*state = State::Address(address);
|
||||
let conf_desc = usb2::configuration::Descriptor {
|
||||
wTotalLength: (usb2::configuration::Descriptor::SIZE
|
||||
+ usb2::interface::Descriptor::SIZE)
|
||||
.into(),
|
||||
bNumInterfaces: NonZeroU8::new(1).unwrap(),
|
||||
bConfigurationValue: core::num::NonZeroU8::new(CONFIG_VAL).unwrap(),
|
||||
iConfiguration: None,
|
||||
bmAttributes: usb2::configuration::bmAttributes {
|
||||
self_powered: true,
|
||||
remote_wakeup: false,
|
||||
},
|
||||
bMaxPower: 250, // 500 mA
|
||||
};
|
||||
|
||||
let iface_desc = usb2::interface::Descriptor {
|
||||
bInterfaceNumber: 0,
|
||||
bAlternativeSetting: 0,
|
||||
bNumEndpoints: 0,
|
||||
bInterfaceClass: 0,
|
||||
bInterfaceSubClass: 0,
|
||||
bInterfaceProtocol: 0,
|
||||
iInterface: None,
|
||||
};
|
||||
|
||||
resp.extend_from_slice(&conf_desc.bytes()).unwrap();
|
||||
resp.extend_from_slice(&iface_desc.bytes()).unwrap();
|
||||
ep0in.start(&resp[..core::cmp::min(resp.len(), length.into())], usbd);
|
||||
} else {
|
||||
// stay in the default state
|
||||
// out of bounds access: stall the endpoint
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
State::Address(..) => {
|
||||
if let Some(address) = address {
|
||||
// use the new address
|
||||
*state = State::Address(address);
|
||||
} else {
|
||||
*state = State::Default;
|
||||
}
|
||||
}
|
||||
_ => return Err(()),
|
||||
},
|
||||
|
||||
// unspecified behavior
|
||||
State::Configured { .. } => return Err(()),
|
||||
}
|
||||
|
||||
// the response to this request is handled in hardware
|
||||
}
|
||||
|
||||
Request::SetConfiguration { value } => {
|
||||
match *state {
|
||||
// unspecified behavior
|
||||
State::Default => return Err(()),
|
||||
|
||||
State::Address(address) => {
|
||||
if let Some(value) = value {
|
||||
if value.get() == CONFIG_VAL {
|
||||
log::info!("entering the configured state");
|
||||
*state = State::Configured { address, value };
|
||||
Request::SetAddress { address } => {
|
||||
match state {
|
||||
State::Default => {
|
||||
if let Some(address) = address {
|
||||
*state = State::Address(address);
|
||||
} else {
|
||||
log::error!("unsupported configuration value");
|
||||
return Err(());
|
||||
// stay in the default state
|
||||
}
|
||||
} else {
|
||||
// stay in the address mode
|
||||
}
|
||||
|
||||
State::Address(..) => {
|
||||
if let Some(address) = address {
|
||||
// use the new address
|
||||
*state = State::Address(address);
|
||||
} else {
|
||||
*state = State::Default;
|
||||
}
|
||||
}
|
||||
|
||||
// unspecified behavior
|
||||
State::Configured { .. } => return Err(()),
|
||||
}
|
||||
|
||||
State::Configured {
|
||||
address,
|
||||
value: curr_value,
|
||||
} => {
|
||||
if let Some(new_value) = value {
|
||||
if new_value.get() == CONFIG_VAL {
|
||||
if new_value != curr_value {
|
||||
log::info!("changing configuration");
|
||||
*state = State::Configured {
|
||||
address,
|
||||
value: new_value,
|
||||
};
|
||||
// the response to this request is handled in hardware
|
||||
}
|
||||
|
||||
Request::SetConfiguration { value } => {
|
||||
match *state {
|
||||
// unspecified behavior
|
||||
State::Default => return Err(()),
|
||||
|
||||
State::Address(address) => {
|
||||
if let Some(value) = value {
|
||||
if value.get() == CONFIG_VAL {
|
||||
defmt::println!("entering the configured state");
|
||||
*state = State::Configured { address, value };
|
||||
} else {
|
||||
defmt::error!("unsupported configuration value");
|
||||
return Err(());
|
||||
}
|
||||
} else {
|
||||
log::error!("unsupported configuration value");
|
||||
return Err(());
|
||||
// stay in the address mode
|
||||
}
|
||||
}
|
||||
|
||||
State::Configured {
|
||||
address,
|
||||
value: curr_value,
|
||||
} => {
|
||||
if let Some(new_value) = value {
|
||||
if new_value.get() == CONFIG_VAL {
|
||||
if new_value != curr_value {
|
||||
defmt::println!("changing configuration");
|
||||
*state = State::Configured {
|
||||
address,
|
||||
value: new_value,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
defmt::error!("unsupported configuration value");
|
||||
return Err(());
|
||||
}
|
||||
} else {
|
||||
defmt::println!("returned to the address state");
|
||||
*state = State::Address(address);
|
||||
}
|
||||
} else {
|
||||
log::info!("returned to the address state");
|
||||
*state = State::Address(address);
|
||||
}
|
||||
}
|
||||
|
||||
usbd.tasks_ep0status
|
||||
.write(|w| w.tasks_ep0status().set_bit());
|
||||
}
|
||||
|
||||
usbd.tasks_ep0status
|
||||
.write(|w| w.tasks_ep0status().set_bit());
|
||||
// stall any other request
|
||||
_ => return Err(()),
|
||||
}
|
||||
|
||||
// stall any other request
|
||||
_ => return Err(()),
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,26 +3,27 @@
|
|||
#![no_std]
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use heapless::{consts, Vec};
|
||||
use panic_log as _; // the panicking behavior
|
||||
use heapless::Vec;
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use firmware as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
dk::init().unwrap();
|
||||
|
||||
// a stack-allocated `Vec` with capacity for 6 bytes
|
||||
let mut buffer = Vec::<u8, consts::U6>::new();
|
||||
// ^^ capacity; this is a type not a value
|
||||
let mut buffer = Vec::<u8, 6>::new();
|
||||
// content type ^^ ^ capacity
|
||||
|
||||
// `heapless::Vec` exposes the same API surface as `std::Vec` but some of its methods return a
|
||||
// `Result` to indicate whether the operation failed due to the `heapless::Vec` being full
|
||||
log::info!("start: {:?}", buffer);
|
||||
defmt::println!("start: {:?}", buffer);
|
||||
|
||||
buffer.push(0).expect("buffer full");
|
||||
log::info!("after `push`: {:?}", buffer);
|
||||
defmt::println!("after `push`: {:?}", buffer);
|
||||
|
||||
buffer.extend_from_slice(&[1, 2, 3]).expect("buffer full");
|
||||
log::info!("after `extend`: {:?}", buffer);
|
||||
defmt::println!("after `extend`: {:?}", &buffer);
|
||||
|
||||
// TODO try this operation
|
||||
// buffer.extend_from_slice(&[4, 5, 6, 7]).expect("buffer full");
|
||||
|
|
20
advanced/firmware/src/lib.rs
Normal file
20
advanced/firmware/src/lib.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
#![no_std]
|
||||
|
||||
use panic_probe as _;
|
||||
|
||||
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
||||
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
||||
#[defmt::panic_handler]
|
||||
fn panic() -> ! {
|
||||
unsafe {
|
||||
// turn off the USB D+ pull-up before pausing the device with a breakpoint
|
||||
// this disconnects the nRF device from the USB host so the USB host won't attempt further
|
||||
// USB communication (and see an unresponsive device). probe-run will also reset the nRF's
|
||||
// USBD peripheral when it sees the device in a halted state which has the same effect as
|
||||
// this line but that can take a while and the USB host may issue a power cycle of the USB
|
||||
// port / hub / root in the meantime, which can bring down the probe and break probe-run
|
||||
const USBD_USBPULLUP: *mut u32 = 0x4002_7504 as *mut u32;
|
||||
USBD_USBPULLUP.write_volatile(0)
|
||||
}
|
||||
cortex_m::asm::udf()
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
# (..)
|
||||
rustflags = [
|
||||
"-C", "linker=flip-link", # adds stack overflow protection
|
||||
"-C", "link-arg=-Tdefmt.x", # defmt support
|
||||
# (..)
|
||||
]
|
||||
|
||||
|
|
|
@ -6,17 +6,18 @@ name = "apps"
|
|||
version = "0.0.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.6.4"
|
||||
cortex-m-rt = "0.6.13"
|
||||
cortex-m = "0.7.3"
|
||||
cortex-m-rt = "0.7.1"
|
||||
dk = { path = "../../boards/dk", features = ["beginner"] }
|
||||
heapless = "0.5.5"
|
||||
log = "0.4.8"
|
||||
panic-log = { path = "../../common/panic-log" }
|
||||
heapless = "0.7.9"
|
||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||
defmt = "0.3.0"
|
||||
defmt-rtt = "0.3.1"
|
||||
|
||||
# optimize code in both profiles
|
||||
[profile.dev]
|
||||
codegen-units = 1
|
||||
debug = 1
|
||||
debug = 2
|
||||
debug-assertions = true # !
|
||||
incremental = false
|
||||
lto = "fat"
|
||||
|
@ -31,3 +32,18 @@ incremental = false
|
|||
lto = "fat"
|
||||
opt-level = 3
|
||||
overflow-checks = false
|
||||
|
||||
[features]
|
||||
# set defmt logging levels here
|
||||
default = [
|
||||
"defmt-default",
|
||||
# "dependency-a/defmt-trace",
|
||||
]
|
||||
|
||||
# do NOT modify these features
|
||||
defmt-default = []
|
||||
defmt-trace = []
|
||||
defmt-debug = []
|
||||
defmt-info = []
|
||||
defmt-warn = []
|
||||
defmt-error = []
|
|
@ -4,12 +4,13 @@
|
|||
use core::time::Duration;
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use panic_log as _; // panic handler
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
// uncomment to enable more verbose logs
|
||||
// log::set_max_level(log::LevelFilter::Trace);
|
||||
// to enable more verbose logs, go to your `Cargo.toml` and set defmt logging levels
|
||||
// to `defmt-trace` by changing the `default = []` entry in `[features]`
|
||||
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
|
@ -19,7 +20,7 @@ fn main() -> ! {
|
|||
for _ in 0..10 {
|
||||
led.toggle();
|
||||
timer.wait(Duration::from_secs(1));
|
||||
log::info!("LED toggled at {:?}", dk::uptime());
|
||||
defmt::println!("LED toggled at {:?}", dk::uptime());
|
||||
}
|
||||
|
||||
dk::exit()
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
|
||||
use cortex_m::asm;
|
||||
use cortex_m_rt::entry;
|
||||
use panic_log as _; // the panicking behavior
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
// the custom entry point
|
||||
// vvvvv
|
||||
|
@ -18,7 +19,7 @@ fn main() -> ! {
|
|||
// initializes the peripherals
|
||||
dk::init().unwrap();
|
||||
|
||||
log::info!("Hello, world!"); // :wave:
|
||||
defmt::println!("Hello, world!"); // :wave:
|
||||
|
||||
loop {
|
||||
// breakpoint: halts the program's execution
|
||||
|
|
|
@ -3,12 +3,13 @@
|
|||
|
||||
use cortex_m::asm;
|
||||
use cortex_m_rt::entry;
|
||||
use panic_log as _; // the panicking behavior
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
// uncomment to make logs more verbose
|
||||
// log::set_max_level(log::LevelFilter::Trace);
|
||||
// to enable more verbose logs, go to your `Cargo.toml` and set defmt logging levels
|
||||
// to `defmt-trace` by changing the `default = []` entry in `[features]`
|
||||
|
||||
let board = dk::init().unwrap();
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
|
||||
use cortex_m::asm;
|
||||
use cortex_m_rt::entry;
|
||||
use panic_log as _; // the panicking behavior
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
|
@ -28,7 +29,7 @@ fn bar() {
|
|||
let array = [0, 1, 2];
|
||||
let x = array[i]; // out of bounds access
|
||||
|
||||
log::info!("{}", x);
|
||||
defmt::println!("{}", x);
|
||||
}
|
||||
|
||||
fn index() -> usize {
|
||||
|
|
|
@ -4,7 +4,8 @@
|
|||
|
||||
use cortex_m_rt::entry;
|
||||
use dk::ieee802154::{Channel, Packet};
|
||||
use panic_log as _; // the panicking behavior
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
const TEN_MS: u32 = 10_000;
|
||||
|
||||
|
@ -37,15 +38,15 @@ fn main() -> ! {
|
|||
if packet.len() == 1 {
|
||||
let destination = packet[0];
|
||||
|
||||
log::info!("{} -> {}", source, destination);
|
||||
defmt::println!("{} -> {}", source, destination);
|
||||
// or cast to `char` for a more readable output
|
||||
log::info!("{:?} -> {:?}", source as char, destination as char);
|
||||
defmt::println!("{:?} -> {:?}", source as char, destination as char);
|
||||
} else {
|
||||
log::error!("response packet was not a single byte");
|
||||
defmt::error!("response packet was not a single byte");
|
||||
dk::exit()
|
||||
}
|
||||
} else {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
|
||||
|
|
|
@ -6,16 +6,18 @@ use cortex_m_rt::entry;
|
|||
// NOTE you can use `FnvIndexMap` instead of `LinearMap`; the former may have better
|
||||
// lookup performance when the dictionary contains a large number of items but performance is
|
||||
// not important for this exercise
|
||||
use heapless::{consts, LinearMap};
|
||||
use panic_log as _; // the panicking behavior
|
||||
use heapless::LinearMap;
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
dk::init().unwrap();
|
||||
|
||||
// a dictionary with capacity for 2 elements
|
||||
let mut dict = LinearMap::<_, _, consts::U2>::new();
|
||||
// ^^ capacity; this is a type not a value
|
||||
let mut dict = LinearMap::<_, _, 2>::new();
|
||||
// content types ^^ ^^ ^ capacity
|
||||
// (inferred by rust)
|
||||
|
||||
// do some insertions
|
||||
dict.insert(b'A', b'*').expect("dictionary full");
|
||||
|
@ -25,9 +27,9 @@ fn main() -> ! {
|
|||
let key = b'A';
|
||||
let value = dict[&key]; // the key needs to be passed by reference
|
||||
|
||||
log::info!("{} -> {}", key, value);
|
||||
defmt::println!("{} -> {}", key, value);
|
||||
// more readable
|
||||
log::info!("{:?} -> {:?}", key as char, value as char);
|
||||
defmt::println!("{:?} -> {:?}", key as char, value as char);
|
||||
|
||||
// TODO try another insertion
|
||||
// TODO try looking up a key not contained in the dictionary
|
||||
|
|
|
@ -4,8 +4,9 @@
|
|||
|
||||
use cortex_m_rt::entry;
|
||||
use dk::ieee802154::{Channel, Packet};
|
||||
use heapless::{consts, LinearMap};
|
||||
use panic_log as _; // the panicking behavior
|
||||
use heapless::LinearMap;
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
const TEN_MS: u32 = 10_000;
|
||||
|
||||
|
@ -19,7 +20,7 @@ fn main() -> ! {
|
|||
radio.set_channel(Channel::_25);
|
||||
|
||||
// capacity (128) should be large enough for the ASCII range
|
||||
let dict = LinearMap::<u8, u8, consts::U128>::new();
|
||||
let mut dict = LinearMap::<u8, u8, 128>::new();
|
||||
|
||||
let mut packet = Packet::new();
|
||||
// TODO do the whole ASCII range [0, 127]
|
||||
|
@ -36,16 +37,18 @@ fn main() -> ! {
|
|||
// TODO insert the key-value pair
|
||||
// dict.insert(/* ? */, /* ? */).expect("dictionary full");
|
||||
} else {
|
||||
log::error!("response packet was not a single byte");
|
||||
defmt::error!("response packet was not a single byte");
|
||||
dk::exit()
|
||||
}
|
||||
} else {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
}
|
||||
|
||||
log::info!("{:?}", dict);
|
||||
defmt::println!("{:?}", defmt::Debug2Format(&dict));
|
||||
// ^^^^^^^^^^^^^^^^^^^ this adapter is currently needed to log `heapless`
|
||||
// data structures (like `LinearMap` here) with `defmt`
|
||||
|
||||
dk::exit()
|
||||
}
|
||||
|
|
|
@ -5,26 +5,30 @@
|
|||
use core::str;
|
||||
|
||||
use cortex_m_rt::entry;
|
||||
use heapless::{consts, Vec};
|
||||
use panic_log as _; // the panicking behavior
|
||||
use heapless::Vec;
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
dk::init().unwrap();
|
||||
|
||||
// a buffer with capacity for 2 bytes
|
||||
let mut buffer = Vec::<u8, consts::U2>::new();
|
||||
// ^^ capacity; this is a type not a value
|
||||
let mut buffer = Vec::<u8, 2>::new();
|
||||
// content type ^^ ^ capacity
|
||||
|
||||
// do some insertions
|
||||
buffer.push(b'H').expect("buffer full");
|
||||
buffer.push(b'i').expect("buffer full");
|
||||
|
||||
// look into the contents so far
|
||||
log::info!("{:?}", buffer);
|
||||
defmt::println!("{}", defmt::Debug2Format(&buffer));
|
||||
// ^^^^^^^^^^^^^^^^^^^ this adapter is currently needed to log
|
||||
// `StandardRequest` with `defmt`
|
||||
|
||||
// or more readable
|
||||
// NOTE as long as you only push bytes in the ASCII range (0..=127) the conversion should work
|
||||
log::info!(
|
||||
// NOTE utf-8 conversion works as long as you only push bytes in the ASCII range (0..=127)
|
||||
defmt::println!(
|
||||
"{}",
|
||||
str::from_utf8(&buffer).expect("content was not UTF-8")
|
||||
);
|
||||
|
|
|
@ -6,8 +6,9 @@ use core::str;
|
|||
|
||||
use cortex_m_rt::entry;
|
||||
use dk::ieee802154::{Channel, Packet};
|
||||
use heapless::{consts, Vec};
|
||||
use panic_log as _; // the panicking behavior
|
||||
use heapless::Vec;
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
const TEN_MS: u32 = 10_000;
|
||||
|
||||
|
@ -27,17 +28,17 @@ fn main() -> ! {
|
|||
radio.send(&mut packet);
|
||||
|
||||
if radio.recv_timeout(&mut packet, &mut timer, TEN_MS).is_err() {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"ciphertext: {}",
|
||||
str::from_utf8(&packet).expect("packet was not valid UTF-8")
|
||||
);
|
||||
|
||||
/* # Decrypt the string */
|
||||
let mut buf = Vec::<u8, consts::U128>::new();
|
||||
let mut buf = Vec::<u8, 128>::new();
|
||||
|
||||
// iterate over the bytes
|
||||
for input in packet.iter() {
|
||||
|
@ -47,7 +48,7 @@ fn main() -> ! {
|
|||
buf.push(output).expect("buffer full");
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"plaintext: {}",
|
||||
str::from_utf8(&buf).expect("buffer contains non-UTF-8 data")
|
||||
);
|
||||
|
|
|
@ -6,8 +6,9 @@ use core::str;
|
|||
|
||||
use cortex_m_rt::entry;
|
||||
use dk::ieee802154::{Channel, Packet};
|
||||
use heapless::{consts, LinearMap, Vec};
|
||||
use panic_log as _; // the panicking behavior
|
||||
use heapless::{LinearMap, Vec};
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
const TEN_MS: u32 = 10_000;
|
||||
|
||||
|
@ -21,7 +22,7 @@ fn main() -> ! {
|
|||
radio.set_channel(Channel::_25);
|
||||
|
||||
/* # Build a dictionary */
|
||||
let dict = LinearMap::<u8, u8, consts::U128>::new();
|
||||
let dict = LinearMap::<u8, u8, 128>::new();
|
||||
|
||||
let mut packet = Packet::new();
|
||||
for source in 0..=127 {
|
||||
|
@ -37,11 +38,11 @@ fn main() -> ! {
|
|||
// TODO insert the key-value pair
|
||||
// dict.insert(/* ? */, /* ? */).expect("dictionary full");
|
||||
} else {
|
||||
log::error!("response packet was not a single byte");
|
||||
defmt::error!("response packet was not a single byte");
|
||||
dk::exit()
|
||||
}
|
||||
} else {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
}
|
||||
|
@ -51,17 +52,17 @@ fn main() -> ! {
|
|||
radio.send(&mut packet);
|
||||
|
||||
if radio.recv_timeout(&mut packet, &mut timer, TEN_MS).is_err() {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"ciphertext: {}",
|
||||
str::from_utf8(&packet).expect("packet was not valid UTF-8")
|
||||
);
|
||||
|
||||
/* # Decrypt the string */
|
||||
let mut buffer = Vec::<u8, consts::U128>::new();
|
||||
let mut buffer = Vec::<u8, 128>::new();
|
||||
|
||||
// iterate over the bytes
|
||||
for byte in packet.iter() {
|
||||
|
@ -71,7 +72,7 @@ fn main() -> ! {
|
|||
buffer.push(value).expect("buffer full");
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"plaintext: {}",
|
||||
str::from_utf8(&buffer).expect("buffer contains non-UTF-8 data")
|
||||
);
|
||||
|
|
|
@ -6,8 +6,9 @@ use core::str;
|
|||
|
||||
use cortex_m_rt::entry;
|
||||
use dk::ieee802154::{Channel, Packet};
|
||||
use heapless::{consts, LinearMap, Vec};
|
||||
use panic_log as _; // the panicking behavior
|
||||
use heapless::{LinearMap, Vec};
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
const TEN_MS: u32 = 10_000;
|
||||
|
||||
|
@ -21,7 +22,7 @@ fn main() -> ! {
|
|||
radio.set_channel(Channel::_25);
|
||||
|
||||
/* # Build a dictionary */
|
||||
let dict = LinearMap::<u8, u8, consts::U128>::new();
|
||||
let dict = LinearMap::<u8, u8, 128>::new();
|
||||
|
||||
let mut packet = Packet::new();
|
||||
for source in 0..=127 {
|
||||
|
@ -37,11 +38,11 @@ fn main() -> ! {
|
|||
// TODO insert the key-value pair
|
||||
// dict.insert(/* ? */, /* ? */).expect("dictionary full");
|
||||
} else {
|
||||
log::error!("response packet was not a single byte");
|
||||
defmt::error!("response packet was not a single byte");
|
||||
dk::exit()
|
||||
}
|
||||
} else {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
}
|
||||
|
@ -51,17 +52,17 @@ fn main() -> ! {
|
|||
radio.send(&mut packet);
|
||||
|
||||
if radio.recv_timeout(&mut packet, &mut timer, TEN_MS).is_err() {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"ciphertext: {}",
|
||||
str::from_utf8(&packet).expect("packet was not valid UTF-8")
|
||||
);
|
||||
|
||||
/* # Decrypt the string */
|
||||
let mut buffer = Vec::<u8, consts::U128>::new();
|
||||
let mut buffer = Vec::<u8, 128>::new();
|
||||
|
||||
// iterate over the bytes
|
||||
for byte in packet.iter() {
|
||||
|
@ -71,7 +72,7 @@ fn main() -> ! {
|
|||
buffer.push(value).expect("buffer full");
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"plaintext: {}",
|
||||
str::from_utf8(&buffer).expect("buffer contains non-UTF-8 data")
|
||||
);
|
||||
|
@ -82,11 +83,11 @@ fn main() -> ! {
|
|||
radio.send(&mut packet);
|
||||
|
||||
if radio.recv_timeout(&mut packet, &mut timer, TEN_MS).is_err() {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"Dongle response: {}",
|
||||
str::from_utf8(&packet).expect("response was not UTF-8")
|
||||
);
|
||||
|
|
|
@ -6,8 +6,9 @@ use core::str;
|
|||
|
||||
use cortex_m_rt::entry;
|
||||
use dk::ieee802154::{Channel, Packet};
|
||||
use heapless::{consts, LinearMap};
|
||||
use panic_log as _; // the panicking behavior
|
||||
use heapless::LinearMap;
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
const TEN_MS: u32 = 10_000;
|
||||
|
||||
|
@ -21,7 +22,7 @@ fn main() -> ! {
|
|||
radio.set_channel(Channel::_25);
|
||||
|
||||
/* # Build a dictionary */
|
||||
let mut dict = LinearMap::<u8, u8, consts::U128>::new();
|
||||
let mut dict = LinearMap::<u8, u8, 128>::new();
|
||||
|
||||
// the IEEE 802.15.4 packet that will carry our data
|
||||
let mut packet = Packet::new();
|
||||
|
@ -38,11 +39,11 @@ fn main() -> ! {
|
|||
dict.insert(cipherletter, plainletter)
|
||||
.expect("dictionary full");
|
||||
} else {
|
||||
log::error!("response packet was not a single byte");
|
||||
defmt::error!("response packet was not a single byte");
|
||||
dk::exit()
|
||||
}
|
||||
} else {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
}
|
||||
|
@ -52,11 +53,11 @@ fn main() -> ! {
|
|||
radio.send(&mut packet);
|
||||
|
||||
if radio.recv_timeout(&mut packet, &mut timer, TEN_MS).is_err() {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"ciphertext: {}",
|
||||
str::from_utf8(&packet).expect("packet was not valid UTF-8")
|
||||
);
|
||||
|
@ -74,7 +75,7 @@ fn main() -> ! {
|
|||
*spot = plainletter;
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"plaintext: {}",
|
||||
str::from_utf8(&packet).expect("buffer contains non-UTF-8 data")
|
||||
);
|
||||
|
@ -83,11 +84,11 @@ fn main() -> ! {
|
|||
radio.send(&mut packet);
|
||||
|
||||
if radio.recv_timeout(&mut packet, &mut timer, TEN_MS).is_err() {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"Dongle response: {}",
|
||||
str::from_utf8(&packet).expect("response was not UTF-8")
|
||||
);
|
||||
|
|
|
@ -6,8 +6,9 @@ use core::str;
|
|||
|
||||
use cortex_m_rt::entry;
|
||||
use dk::ieee802154::{Channel, Packet};
|
||||
use heapless::{consts, LinearMap, Vec};
|
||||
use panic_log as _; // the panicking behavior
|
||||
use heapless::{LinearMap, Vec};
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
const TEN_MS: u32 = 10_000;
|
||||
|
||||
|
@ -21,8 +22,8 @@ fn main() -> ! {
|
|||
radio.set_channel(Channel::_25);
|
||||
|
||||
/* # Build a dictionary */
|
||||
let mut dict = LinearMap::<u8, u8, consts::U128>::new();
|
||||
// ^^^^^^^^^^^^ NOTE larger capacity
|
||||
let mut dict = LinearMap::<u8, u8, 128>::new();
|
||||
// ^^^ NOTE larger capacity
|
||||
|
||||
// the IEEE 802.15.4 packet that will carry our data
|
||||
let mut packet = Packet::new();
|
||||
|
@ -41,11 +42,11 @@ fn main() -> ! {
|
|||
dict.insert(cipherletter, plainletter)
|
||||
.expect("dictionary full");
|
||||
} else {
|
||||
log::error!("response packet was not a single byte");
|
||||
defmt::error!("response packet was not a single byte");
|
||||
dk::exit()
|
||||
}
|
||||
} else {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
}
|
||||
|
@ -55,17 +56,17 @@ fn main() -> ! {
|
|||
radio.send(&mut packet);
|
||||
|
||||
if radio.recv_timeout(&mut packet, &mut timer, TEN_MS).is_err() {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"ciphertext: {}",
|
||||
str::from_utf8(&packet).expect("packet was not valid UTF-8")
|
||||
);
|
||||
|
||||
/* # Decrypt the string */
|
||||
let mut buffer = Vec::<u8, consts::U128>::new();
|
||||
let mut buffer = Vec::<u8, 128>::new();
|
||||
|
||||
// iterate over the bytes
|
||||
for cipherletter in packet.iter() {
|
||||
|
@ -76,7 +77,7 @@ fn main() -> ! {
|
|||
buffer.push(plainletter).expect("buffer full");
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"plaintext: {}",
|
||||
str::from_utf8(&buffer).expect("buffer contains non-UTF-8 data")
|
||||
);
|
||||
|
@ -87,11 +88,11 @@ fn main() -> ! {
|
|||
radio.send(&mut packet);
|
||||
|
||||
if radio.recv_timeout(&mut packet, &mut timer, TEN_MS).is_err() {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
dk::exit()
|
||||
}
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"Dongle response: {}",
|
||||
str::from_utf8(&packet).expect("response was not UTF-8")
|
||||
);
|
||||
|
|
|
@ -6,7 +6,8 @@ use core::str;
|
|||
|
||||
use cortex_m_rt::entry;
|
||||
use dk::ieee802154::{Channel, Packet};
|
||||
use panic_log as _; // the panicking behavior
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
const TEN_MS: u32 = 10_000;
|
||||
|
||||
|
@ -33,19 +34,19 @@ fn main() -> ! {
|
|||
// let msg = b"Hello?";
|
||||
|
||||
packet.copy_from_slice(msg);
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"sending: {}",
|
||||
str::from_utf8(msg).expect("msg was not valid UTF-8 data")
|
||||
);
|
||||
|
||||
radio.send(&mut packet);
|
||||
if radio.recv_timeout(&mut packet, &mut timer, TEN_MS).is_ok() {
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"received: {}",
|
||||
str::from_utf8(&packet).expect("response was not valid UTF-8 data")
|
||||
);
|
||||
} else {
|
||||
log::error!("no response or response packet was corrupted");
|
||||
defmt::error!("no response or response packet was corrupted");
|
||||
}
|
||||
dk::exit()
|
||||
}
|
||||
|
|
|
@ -6,7 +6,8 @@ use core::str;
|
|||
|
||||
use cortex_m_rt::entry;
|
||||
use dk::ieee802154::{Channel, Error, Packet};
|
||||
use panic_log as _; // the panicking behavior
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
const TEN_MS: u32 = 10_000;
|
||||
|
||||
|
@ -24,7 +25,7 @@ fn main() -> ! {
|
|||
let msg = b"olleh";
|
||||
packet.copy_from_slice(msg);
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"sending: {}",
|
||||
str::from_utf8(msg).expect("message is not valid UTF-8")
|
||||
);
|
||||
|
@ -37,14 +38,15 @@ fn main() -> ! {
|
|||
|
||||
match res {
|
||||
Ok(crc) => {
|
||||
log::info!(
|
||||
"received: {} (CRC={})",
|
||||
defmt::println!(
|
||||
"received: {} (CRC = {:X})",
|
||||
// ^^ print as uppercase hexadecimal
|
||||
str::from_utf8(&*packet).expect("response is not valid UTF-8"),
|
||||
crc
|
||||
);
|
||||
}
|
||||
Err(Error::Crc(crc)) => log::error!("invalid CRC: {:06x}", crc),
|
||||
Err(Error::Timeout) => log::error!("no response within {} ms", TEN_MS / 1_000),
|
||||
Err(Error::Crc(crc)) => defmt::error!("invalid CRC: {:X}", crc),
|
||||
Err(Error::Timeout) => defmt::error!("no response within {} ms", TEN_MS / 1_000),
|
||||
}
|
||||
|
||||
dk::exit()
|
||||
|
|
|
@ -6,7 +6,8 @@ use core::str;
|
|||
|
||||
use cortex_m_rt::entry;
|
||||
use dk::ieee802154::{Channel, Packet, TxPower};
|
||||
use panic_log as _; // the panicking behavior
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
|
@ -25,7 +26,7 @@ fn main() -> ! {
|
|||
// let msg: &[u8; 5] = &[b'H', b'e', b'l', b'l', b'o'];
|
||||
// let msg: &[u8; 5] = b"Hello";
|
||||
|
||||
log::info!(
|
||||
defmt::println!(
|
||||
"sending: {}",
|
||||
str::from_utf8(msg).expect("msg is not valid UTF-8 data")
|
||||
);
|
||||
|
|
|
@ -3,14 +3,15 @@
|
|||
|
||||
use cortex_m::asm;
|
||||
use cortex_m_rt::entry;
|
||||
use panic_log as _; // panic handler
|
||||
// this imports `beginner/apps/lib.rs` to retrieve our global logger + panicking-behavior
|
||||
use apps as _;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
// board initialization
|
||||
dk::init().unwrap();
|
||||
|
||||
log::info!("fib(100) = {:?}", fib(100));
|
||||
fib(100);
|
||||
|
||||
loop {
|
||||
asm::bkpt();
|
||||
|
@ -20,7 +21,8 @@ fn main() -> ! {
|
|||
#[inline(never)]
|
||||
fn fib(n: u32) -> u32 {
|
||||
// allocate and initialize one kilobyte of stack memory to provoke stack overflow
|
||||
let _use_stack = [0xAA; 1024];
|
||||
let use_stack = [0xAA; 1024];
|
||||
defmt::println!("allocating [{}; 1024]; round #{}", use_stack[1023], n);
|
||||
|
||||
if n < 2 {
|
||||
1
|
||||
|
|
10
beginner/apps/src/lib.rs
Normal file
10
beginner/apps/src/lib.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
#![no_std]
|
||||
|
||||
use panic_probe as _;
|
||||
|
||||
// same panicking *behavior* as `panic-probe` but doesn't print a panic message
|
||||
// this prevents the panic message being printed *twice* when `defmt::panic` is invoked
|
||||
#[defmt::panic_handler]
|
||||
fn panic() -> ! {
|
||||
cortex_m::asm::udf()
|
||||
}
|
|
@ -6,13 +6,28 @@ name = "dk"
|
|||
version = "0.0.0"
|
||||
|
||||
[dependencies]
|
||||
cortex-m = "0.6.4"
|
||||
cortex-m-rt = "0.6.13"
|
||||
embedded-hal = "0.2.3"
|
||||
hal = { package = "nrf52840-hal", version = "0.12.1" }
|
||||
log = "0.4.8"
|
||||
rtt-target = { version = "0.2.0", features = ["cortex-m"] }
|
||||
cortex-m = "0.7.3"
|
||||
cortex-m-rt = "0.7.1"
|
||||
embedded-hal = "0.2.6"
|
||||
hal = { package = "nrf52840-hal", version = "0.14.0" }
|
||||
panic-probe = { version = "0.3.0", features = ["print-defmt"] }
|
||||
defmt = "0.3.0"
|
||||
defmt-rtt = "0.3.1"
|
||||
|
||||
[features]
|
||||
advanced = []
|
||||
beginner = []
|
||||
advanced = []
|
||||
|
||||
# set defmt logging levels here
|
||||
default = [
|
||||
"defmt-info",
|
||||
# "dependency-a/defmt-trace",
|
||||
]
|
||||
|
||||
# do NOT modify these features
|
||||
defmt-default = []
|
||||
defmt-trace = []
|
||||
defmt-debug = []
|
||||
defmt-info = []
|
||||
defmt-warn = []
|
||||
defmt-error = []
|
||||
|
|
|
@ -21,10 +21,10 @@ use hal::{
|
|||
rtc::{Rtc, RtcInterrupt},
|
||||
timer::OneShot,
|
||||
};
|
||||
use log::{LevelFilter, Log};
|
||||
use rtt_target::rprintln;
|
||||
|
||||
use defmt;
|
||||
#[cfg(any(feature = "beginner", feature = "advanced"))]
|
||||
use rtt_target::rtt_init_print;
|
||||
use defmt_rtt as _; // global logger
|
||||
|
||||
#[cfg(feature = "advanced")]
|
||||
use crate::{
|
||||
|
@ -79,7 +79,7 @@ pub struct Led {
|
|||
impl Led {
|
||||
/// Turns on the LED
|
||||
pub fn on(&mut self) {
|
||||
log::trace!(
|
||||
defmt::trace!(
|
||||
"setting P{}.{} low (LED on)",
|
||||
if self.inner.port() == Port::Port1 {
|
||||
'1'
|
||||
|
@ -95,7 +95,7 @@ impl Led {
|
|||
|
||||
/// Turns off the LED
|
||||
pub fn off(&mut self) {
|
||||
log::trace!(
|
||||
defmt::trace!(
|
||||
"setting P{}.{} high (LED off)",
|
||||
if self.inner.port() == Port::Port1 {
|
||||
'1'
|
||||
|
@ -137,7 +137,7 @@ pub struct Timer {
|
|||
impl Timer {
|
||||
/// Blocks program execution for at least the specified `duration`
|
||||
pub fn wait(&mut self, duration: Duration) {
|
||||
log::trace!("blocking for {:?} ...", duration);
|
||||
defmt::trace!("blocking for {:?} ...", duration);
|
||||
|
||||
// 1 cycle = 1 microsecond
|
||||
const NANOS_IN_ONE_MICRO: u32 = 1_000;
|
||||
|
@ -164,7 +164,7 @@ impl Timer {
|
|||
self.inner.delay(cycles)
|
||||
}
|
||||
|
||||
log::trace!("... DONE");
|
||||
defmt::trace!("... DONE");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,21 +195,7 @@ pub fn init() -> Result<Board, ()> {
|
|||
Clocks<clocks::ExternalOscillator, clocks::ExternalOscillator, clocks::LfOscStarted>,
|
||||
> = None;
|
||||
|
||||
// NOTE this must be executed as early as possible or the tool will timeout
|
||||
// NOTE the unsafety of this macro is incorrect; it must be run at most once
|
||||
#[cfg(feature = "beginner")]
|
||||
rtt_init_print!(BlockIfFull, 16384);
|
||||
#[cfg(feature = "advanced")]
|
||||
rtt_init_print!(NoBlockSkip, 16384);
|
||||
|
||||
log::set_logger(&Logger).unwrap();
|
||||
|
||||
// if not configured in the application we default to the `Info` level
|
||||
if log::max_level() == LevelFilter::Off {
|
||||
log::set_max_level(LevelFilter::Info)
|
||||
}
|
||||
|
||||
log::debug!("Initializing the board");
|
||||
defmt::debug!("Initializing the board");
|
||||
|
||||
let clocks = Clocks::new(periph.CLOCK);
|
||||
let clocks = clocks.enable_ext_hfosc();
|
||||
|
@ -220,7 +206,7 @@ pub fn init() -> Result<Board, ()> {
|
|||
#[cfg(feature = "beginner")]
|
||||
let clocks = unsafe { CLOCKS.get_or_insert(_clocks) };
|
||||
|
||||
log::debug!("Clocks configured");
|
||||
defmt::debug!("Clocks configured");
|
||||
|
||||
let mut rtc = Rtc::new(periph.RTC0, 0).unwrap();
|
||||
rtc.enable_interrupt(RtcInterrupt::Overflow, None);
|
||||
|
@ -239,7 +225,7 @@ pub fn init() -> Result<Board, ()> {
|
|||
// mechanism)
|
||||
unsafe { NVIC::unmask(Interrupt::RTC0) };
|
||||
|
||||
log::debug!("RTC started");
|
||||
defmt::debug!("RTC started");
|
||||
|
||||
let pins = p0::Parts::new(periph.P0);
|
||||
|
||||
|
@ -249,7 +235,7 @@ pub fn init() -> Result<Board, ()> {
|
|||
let _3 = pins.p0_15.degrade().into_push_pull_output(Level::High);
|
||||
let _4 = pins.p0_16.degrade().into_push_pull_output(Level::High);
|
||||
|
||||
log::debug!("I/O pins have been configured for digital output");
|
||||
defmt::debug!("I/O pins have been configured for digital output");
|
||||
|
||||
let timer = hal::Timer::new(periph.TIMER0);
|
||||
|
||||
|
@ -259,7 +245,9 @@ pub fn init() -> Result<Board, ()> {
|
|||
|
||||
// set TX power to its maximum value
|
||||
radio.set_txpower(ieee802154::TxPower::Pos8dBm);
|
||||
log::debug!("Radio initialized and configured with TX power set to the maximum value");
|
||||
defmt::debug!(
|
||||
"Radio initialized and configured with TX power set to the maximum value"
|
||||
);
|
||||
radio
|
||||
};
|
||||
|
||||
|
@ -285,29 +273,6 @@ pub fn init() -> Result<Board, ()> {
|
|||
}
|
||||
}
|
||||
|
||||
struct Logger;
|
||||
|
||||
impl Log for Logger {
|
||||
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||||
metadata.level() <= log::STATIC_MAX_LEVEL
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
if !self.enabled(record.metadata()) {
|
||||
return;
|
||||
}
|
||||
|
||||
rprintln!(
|
||||
"{}:{} -- {}",
|
||||
record.level(),
|
||||
record.target(),
|
||||
record.args()
|
||||
);
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
|
||||
// Counter of OVERFLOW events -- an OVERFLOW occurs every (1<<24) ticks
|
||||
static OVERFLOWS: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
|
@ -323,7 +288,17 @@ fn RTC0() {
|
|||
|
||||
/// Exits the application when the program is executed through the `probe-run` Cargo runner
|
||||
pub fn exit() -> ! {
|
||||
log::info!("`dk::exit() called; exiting ...`");
|
||||
unsafe {
|
||||
// turn off the USB D+ pull-up before pausing the device with a breakpoint
|
||||
// this disconnects the nRF device from the USB host so the USB host won't attempt further
|
||||
// USB communication (and see an unresponsive device). probe-run will also reset the nRF's
|
||||
// USBD peripheral when it sees the device in a halted state which has the same effect as
|
||||
// this line but that can take a while and the USB host may issue a power cycle of the USB
|
||||
// port / hub / root in the meantime, which can bring down the probe and break probe-run
|
||||
const USBD_USBPULLUP: *mut u32 = 0x4002_7504 as *mut u32;
|
||||
USBD_USBPULLUP.write_volatile(0)
|
||||
}
|
||||
defmt::println!("`dk::exit()` called; exiting ...");
|
||||
// force any pending memory operation to complete before the BKPT instruction that follows
|
||||
atomic::compiler_fence(Ordering::SeqCst);
|
||||
loop {
|
||||
|
|
|
@ -51,7 +51,7 @@ impl Ep0In {
|
|||
|
||||
self.busy = true;
|
||||
|
||||
log::info!("EP0IN: start {}B transfer", n);
|
||||
defmt::println!("EP0IN: start {}B transfer", n);
|
||||
|
||||
// start DMA transfer
|
||||
dma_start();
|
||||
|
@ -75,7 +75,7 @@ impl Ep0In {
|
|||
usbd.events_ep0datadone.reset();
|
||||
|
||||
self.busy = false;
|
||||
log::info!("EP0IN: transfer done");
|
||||
defmt::println!("EP0IN: transfer done");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +110,7 @@ pub fn init(power: POWER, usbd: &USBD) {
|
|||
// wait until the USB cable has been connected
|
||||
while power.events_usbdetected.read().bits() == 0 {
|
||||
if once {
|
||||
log::info!("waiting for USB connection on port J3");
|
||||
defmt::println!("waiting for USB connection on port J3");
|
||||
once = false;
|
||||
}
|
||||
|
||||
|
@ -161,7 +161,7 @@ pub fn ep0stall(usbd: &USBD) {
|
|||
}
|
||||
|
||||
/// USBD.EVENTS registers mapped to an enum
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, defmt::Format)]
|
||||
pub enum Event {
|
||||
/// `EVENTS_USBRESET` register was active
|
||||
UsbReset,
|
||||
|
|
|
@ -7,4 +7,20 @@ version = "0.0.0"
|
|||
|
||||
[dependencies]
|
||||
cortex-m = "0.6.4"
|
||||
log = "0.4.8"
|
||||
defmt = "0.2.1"
|
||||
|
||||
|
||||
[features]
|
||||
# set defmt logging levels here
|
||||
default = [
|
||||
"defmt-debug",
|
||||
# "dependency-a/defmt-trace",
|
||||
]
|
||||
|
||||
# do NOT modify these features
|
||||
defmt-default = []
|
||||
defmt-trace = []
|
||||
defmt-debug = []
|
||||
defmt-info = []
|
||||
defmt-warn = []
|
||||
defmt-error = []
|
|
@ -6,7 +6,7 @@ use cortex_m::asm;
|
|||
|
||||
#[panic_handler]
|
||||
fn panic(info: &PanicInfo) -> ! {
|
||||
log::error!("{}", info);
|
||||
defmt::error!("{}", defmt::Debug2Format(&info));
|
||||
|
||||
// abort instruction: triggers a HardFault exception which causes probe-run to exit
|
||||
asm::udf()
|
||||
|
|
|
@ -46,18 +46,25 @@ The device descriptor is 18 bytes long but the host may ask for fewer bytes (see
|
|||
Once you have successfully responded to the GET_DESCRIPTOR Device request you should get logs like these (if you are logging like `usb-3` does):
|
||||
|
||||
``` console
|
||||
INFO:usb_3 -- USB: UsbReset @ 342.071532ms
|
||||
INFO:usb_3 -- USB: UsbEp0Setup @ 414.855956ms
|
||||
INFO:usb_3 -- SETUP: bmrequesttype: 128, brequest: 6, wlength: 64, windex: 0, wvalue: 256
|
||||
INFO:usb_3 -- GET_DESCRIPTOR Device [length=64]
|
||||
INFO:dk::usbd -- EP0IN: start 18B transfer
|
||||
INFO:usb_3 -- USB: UsbEp0DataDone @ 415.222166ms
|
||||
INFO:dk::usbd -- EP0IN: transfer done
|
||||
INFO:usb_3 -- USB: UsbReset @ 465.637206ms
|
||||
INFO:usb_3 -- USB: UsbEp0Setup @ 538.208007ms
|
||||
INFO:usb_3 -- SETUP: bmrequesttype: 0, brequest: 5, wlength: 0, windex: 0, wvalue: 27
|
||||
ERROR:usb_3 -- unknown request (goal achieved if GET_DESCRIPTOR Device was handled)
|
||||
INFO:dk -- `dk::exit() called; exiting ...`
|
||||
USB: UsbReset @ Duration { secs: 0, nanos: 211334227 }
|
||||
USB: UsbEp0Setup @ Duration { secs: 0, nanos: 252380370 }
|
||||
SETUP: bmrequesttype: 0, brequest: 5, wlength: 0, windex: 0, wvalue: 52
|
||||
USB: UsbEp0Setup @ Duration { secs: 0, nanos: 254577635 }
|
||||
SETUP: bmrequesttype: 128, brequest: 6, wlength: 8, windex: 0, wvalue: 256
|
||||
GET_DESCRIPTOR Device [length=8]
|
||||
EP0IN: start 8B transfer
|
||||
USB: UsbEp0DataDone @ Duration { secs: 0, nanos: 254852293 }
|
||||
EP0IN: transfer done
|
||||
USB: UsbEp0Setup @ Duration { secs: 0, nanos: 257568358 }
|
||||
SETUP: bmrequesttype: 128, brequest: 6, wlength: 18, windex: 0, wvalue: 256
|
||||
GET_DESCRIPTOR Device [length=18]
|
||||
EP0IN: start 18B transfer
|
||||
USB: UsbEp0DataDone @ Duration { secs: 0, nanos: 257843016 }
|
||||
EP0IN: transfer done
|
||||
USB: UsbEp0Setup @ Duration { secs: 0, nanos: 259674071 }
|
||||
SETUP: bmrequesttype: 128, brequest: 6, wlength: 9, windex: 0, wvalue: 512
|
||||
ERROR unknown request (goal achieved if GET_DESCRIPTOR Device was handled before)
|
||||
`dk::exit()` called; exiting ...
|
||||
```
|
||||
|
||||
A solution to this exercise can be found in `src/bin/usb-3-solution.rs`.
|
||||
|
|
|
@ -10,7 +10,7 @@ fn on_event(/* parameters */) {
|
|||
if ep0setup(/* arguments */).is_err() {
|
||||
// unsupported or invalid request:
|
||||
// TODO add code to stall the endpoint
|
||||
log::warn!("EP0: unexpected request; stalling the endpoint");
|
||||
defmt::warn!("EP0IN: unexpected request; stalling the endpoint");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,4 +32,4 @@ Note that there's a difference between the error handling done here and the erro
|
|||
(3) stopping the program, and e.g. requiring the user to reset it to make it work again, may not be desirable behavior.
|
||||
For these reasons in embedded software errors tend to be handled as early as possible rather than propagated all the way up.
|
||||
|
||||
This does not preclude error *reporting*. The above snippet includes error reporting in the form of a `log::warn!` statement. This log statement may not be included in the final release of the program as it may not be useful, or even visible, to an end user but it is useful during development.
|
||||
This does not preclude error *reporting*. The above snippet includes error reporting in the form of a `defmt::warn!` statement. This log statement may not be included in the final release of the program as it may not be useful, or even visible, to an end user but it is useful during development.
|
||||
|
|
|
@ -147,11 +147,11 @@ Install the [`flip-link`](https://crates.io/crates/flip-link) and [`probe-run`](
|
|||
|
||||
$ cargo install probe-run
|
||||
(..)
|
||||
Installed package `probe-run v0.1.8` (..)
|
||||
Installed package `probe-run v0.3.1` (..)
|
||||
|
||||
$ cargo install flip-link
|
||||
(..)
|
||||
Installed package `flip-link v0.1.2` (..)
|
||||
Installed package `flip-link v0.1.5` (..)
|
||||
|
||||
$ cargo install nrfdfu
|
||||
(..)
|
||||
|
|
|
@ -10,7 +10,7 @@ Inside the 'configuration 1' rectangle there are two rectangles labeled 'control
|
|||
Inside the 'interface 0' rectangle there are three rectangles labeled 'endpoint 1 IN', 'endpoint 2 IN' and 'endpoint 2 OUT'. Between these three rectangle there is a label that says 'bNumEndpoints=3'; it indicates that this interface has only three endpoints.">
|
||||
<p>
|
||||
|
||||
An interface is closest to a USB device's function. For example, a USB mouse may expose a single HID (Human Interface Device) interface to report user input to the host. USB devices can expose multiple interfaces within a configuration. For example, the nRF52840 Dongle could expose both a CDC ACM interface (AKA virtual serial port) *and* a HID interface; the first interface could be used for (`log::info!`-style) logs; and the second one could provide a RPC (Remote Procedure Call) interface to the host for controlling the nRF52840's radio.
|
||||
An interface is closest to a USB device's function. For example, a USB mouse may expose a single HID (Human Interface Device) interface to report user input to the host. USB devices can expose multiple interfaces within a configuration. For example, the nRF52840 Dongle could expose both a CDC ACM interface (AKA virtual serial port) *and* a HID interface; the first interface could be used for (`defmt::println!`-style) logs; and the second one could provide a RPC (Remote Procedure Call) interface to the host for controlling the nRF52840's radio.
|
||||
|
||||
An interface is made up of one or more *endpoints*. To give an example, a HID interface can use two (interrupt) endpoints, one IN and one OUT, for bidirectional communication with the host. A single endpoint cannot be used by more than one interface with the exception of the special "endpoint 0", which can be (and usually is) shared by all interfaces.
|
||||
|
||||
|
|
|
@ -32,11 +32,11 @@ let array1: [u8; 3] = [0, 1, 2];
|
|||
let array2: [u8; 4] = [0, 1, 2, 3];
|
||||
|
||||
let mut slice: &[u8] = &array1;
|
||||
log::info!("{:?}", slice); // length = 3
|
||||
defmt::println!("{:?}", slice); // length = 3
|
||||
|
||||
// now point to the other array
|
||||
slice = &array2;
|
||||
log::info!("{:?}", slice); // length = 4
|
||||
defmt::println!("{:?}", slice); // length = 4
|
||||
```
|
||||
|
||||
## Byte literals
|
||||
|
@ -82,8 +82,8 @@ On the other hand, `"Hello"` is a string literal with type `&str`. `str` strings
|
|||
|
||||
In this workshop we'll work with ASCII strings so byte string literals that contain no escaped characters are OK to use as packet payloads.
|
||||
|
||||
You'll note that `log::info!("{:?}", b"Hello")` will print `[72, 101, 108, 108, 111]` rather than `"Hello"` and that the `{}` format specifier (`Display`) does not work. This is because the type of the literal is `&[u8; N]` and in Rust this type means "bytes"; those bytes could be ASCII data, UTF-8 data or something else.
|
||||
You'll note that `defmt::println!("{:?}", b"Hello")` will print `[72, 101, 108, 108, 111]` rather than `"Hello"` and that the `{}` format specifier (`Display`) does not work. This is because the type of the literal is `&[u8; N]` and in Rust this type means "bytes"; those bytes could be ASCII data, UTF-8 data or something else.
|
||||
|
||||
To print this you'll need to convert the slice `&[u8]` into a string (`&str`) using the `str::from_utf8` function. This function will verify that the slice contains well formed UTF-8 data and interpret it as a UTF-8 string (`&str`). As long as we use ASCII data (printable ASCII characters) this conversion will not fail.
|
||||
|
||||
Something similar will happen with byte literals: `log::info!("{}", b'A')` will print `65` rather than `A`. To get the `A` output you can cast the byte literal (`u8` value) to the `char` type: `log::info!("{}", b'A' as char)`.
|
||||
Something similar will happen with byte literals: `defmt::println!("{}", b'A')` will print `65` rather than `A`. To get the `A` output you can cast the byte literal (`u8` value) to the `char` type: `defmt::println!("{}", b'A' as char)`.
|
|
@ -5,39 +5,45 @@
|
|||
This program attempts to index an array beyond its length and this results in a panic.
|
||||
|
||||
``` console
|
||||
(HOST) INFO flashing program (34.79 KiB)
|
||||
(HOST) INFO success!
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
ERROR:panic_log -- panicked at 'index out of bounds: the len is 3 but the index is 3', src/bin/panic.rs:29:13
|
||||
ERROR panicked at 'index out of bounds: the len is 3 but the index is 3', src/bin/panic.rs:32:13
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
stack backtrace:
|
||||
0: HardFaultTrampoline
|
||||
<exception entry>
|
||||
[...]
|
||||
1: lib::inline::__udf
|
||||
at ./asm/inline.rs:172:5
|
||||
2: __udf
|
||||
at ./asm/lib.rs:49:17
|
||||
3: cortex_m::asm::udf
|
||||
at /Users/name/.cargo/registry/src/github.com-1ecc6299db9ec823/cortex-m-0.7.3/src/asm.rs:43:5
|
||||
4: rust_begin_unwind
|
||||
at /Users/name/.cargo/registry/src/github.com-1ecc6299db9ec823/panic-probe-0.3.0/src/lib.rs:72:9
|
||||
5: core::panicking::panic_fmt
|
||||
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:100:14
|
||||
6: core::panicking::panic_bounds_check
|
||||
at /rustc/f1edd0429582dd29cccacaf50fd134b05593bd9c/library/core/src/panicking.rs:76:5
|
||||
7: panic::bar
|
||||
at src/bin/panic.rs:29:13
|
||||
at src/bin/panic.rs:32:13
|
||||
8: panic::foo
|
||||
at src/bin/panic.rs:22:5
|
||||
at src/bin/panic.rs:25:5
|
||||
9: panic::__cortex_m_rt_main
|
||||
at src/bin/panic.rs:12:5
|
||||
at src/bin/panic.rs:15:5
|
||||
10: main
|
||||
at src/bin/panic.rs:8:1
|
||||
[...]
|
||||
at src/bin/panic.rs:11:1
|
||||
11: Reset
|
||||
(HOST) ERROR the program panicked
|
||||
```
|
||||
|
||||
In `no_std` programs the behavior of panic is defined using the `#[panic_handler]` attribute. In the example, the *panic handler* is defined in the `panic_log` crate but we can also implement it manually:
|
||||
|
||||
✅ Comment out the `panic_log` import and add the following function to the example:
|
||||
✅ Comment out the `panic_probe` import and add the following function to the example:
|
||||
|
||||
``` rust
|
||||
#[panic_handler]
|
||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
log::error!("{}", info);
|
||||
loop {
|
||||
asm::bkpt()
|
||||
}
|
||||
defmt::panic!("{}", info);
|
||||
}
|
||||
```
|
||||
|
||||
Now run the program again. Try changing the format string of the `error!` macro.
|
||||
Now run the program again. Try changing the format string of the `panic!` macro.
|
||||
|
|
|
@ -56,7 +56,7 @@ fn main() -> ! {
|
|||
|
||||
for plainletter in 0..=127 {
|
||||
/* ... send letter to dongle ... */
|
||||
log::info!("got response");
|
||||
defmt::println!("got response");
|
||||
/* ... store output ... */
|
||||
|
||||
timer.wait(Duration::from_millis(20));
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
``` console
|
||||
$ cargo xtask serial-term
|
||||
deviceid=588c06af0877c8f2 channel=20 TxPower=+8dBm app=loopback.hex
|
||||
received 5 bytes (LQI=49)
|
||||
received 5 bytes (CRC=Ok(0xdad9), LQI=53)
|
||||
```
|
||||
|
||||
The program broadcasts a radio packet that contains the 5-byte string `Hello` over channel 20 (which has a center frequency of 2450 MHz). The `loopback` program running on the Dongle is listening to all packets sent over channel 20; every time it receives a new packet it reports its length and the Link Quality Indicator (LQI) metric of the transmission over the USB/serial interface. As the name implies the LQI metric indicates how good the connection between the sender and the receiver is.
|
||||
|
|
|
@ -14,11 +14,22 @@ RTIC makes a clearer distinction between the application's initialization phase,
|
|||
|
||||
You can use `rustfmt` on `target/rtic-expansion.rs` to make the generated code easier to read. Among other things, the file should contain the following lines. Note that interrupts are disabled during the execution of the `init` function:
|
||||
|
||||
``` rust
|
||||
fn main() -> ! {
|
||||
```rust
|
||||
unsafe extern "C" fn main() -> ! {
|
||||
rtic::export::interrupt::disable();
|
||||
let late = init(init::Context::new(/* .. */));
|
||||
rtic::export::interrupt::enable();
|
||||
idle(idle::Context::new(/* .. */))
|
||||
let mut core: rtic::export::Peripherals = rtic::export::Peripherals::steal().into();
|
||||
#[inline(never)]
|
||||
fn __rtic_init_resources<F>(f: F)
|
||||
where
|
||||
F: FnOnce(),
|
||||
{
|
||||
f();
|
||||
}
|
||||
__rtic_init_resources(|| {
|
||||
let (shared_resources, local_resources, mut monotonics) =
|
||||
init(init::Context::new(core.into()));
|
||||
rtic::export::interrupt::enable();
|
||||
});
|
||||
idle(idle::Context::new(&rtic::export::Priority::new(0)))
|
||||
}
|
||||
```
|
||||
```
|
|
@ -1,5 +1,27 @@
|
|||
# Running the Program
|
||||
|
||||
## Setting the log level
|
||||
|
||||
Enter the appropriate command into the terminal you're using. This will set the log level for this session.
|
||||
|
||||
### MacOS & Linux
|
||||
|
||||
```console
|
||||
$ export DEFMT_LOG=warn
|
||||
```
|
||||
|
||||
### PowerShell
|
||||
```console
|
||||
$ Env: DEFMT_LOG = "warn"
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
||||
```console
|
||||
$ set DEFMT_LOG=warn
|
||||
```
|
||||
## Running the Program
|
||||
|
||||
✅ Open the `src/bin/hello.rs` file and click the "Run" button that's hovering over the `main` function.
|
||||
|
||||
> Note: you will get the "Run" button if the Rust analyzer's workspace is set to the `beginner/apps` folder. This will be the case if the current folder in VS code (left side panel) is set to `beginner/apps`.
|
||||
|
@ -13,14 +35,13 @@ Expected output:
|
|||
|
||||
``` console
|
||||
$ cargo run --bin hello
|
||||
Running `probe-run --chip nRF52840_xxAA target/thumbv7em-none-eabihf/debug/hello`
|
||||
(HOST) INFO flashing program (34.79 KiB)
|
||||
Running `probe-run --chip nRF52840_xxAA target/thumbv7em-none-eabihf/debug/hello`
|
||||
(HOST) INFO flashing program (2 pages / 16.00 KiB)
|
||||
(HOST) INFO success!
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
INFO:hello -- Hello, world!
|
||||
────────────────────────────────────────────────────────────────────────────────
|
||||
(HOST) INFO device halted without error
|
||||
|
||||
```
|
||||
|
||||
`cargo run` will compile the application and then invoke the `probe-run` tool with its argument set to the path of the output ELF file.
|
||||
|
@ -29,7 +50,7 @@ The `probe-run` tool will
|
|||
- flash (load) the program on the microcontroller
|
||||
- reset the microcontroller to make it execute the new program
|
||||
- collect logs from the microcontroller and print them to the console
|
||||
- print a backtrace of the program and exit when the devices reaches a breakpoint (`asm::bkpt()`)
|
||||
- print a backtrace of the program if the halt was due to an error.
|
||||
|
||||
Should you need to configure the `probe-run` invocation to e.g. flash a different microcontroller you can do that in the `.cargo/config.toml` file.
|
||||
|
||||
|
|
|
@ -72,12 +72,14 @@ modify `usb-2.rs` to read `USBD` registers and parse the SETUP data when an EP0S
|
|||
When you have successfully received a GET_DESCRIPTOR request for a Device descriptor you are done. You should see an output like this:
|
||||
|
||||
``` console
|
||||
INFO:usb_2 -- USB: UsbReset @ 438.842772ms
|
||||
INFO:usb_2 -- USB: UsbEp0Setup @ 514.984128ms
|
||||
...
|
||||
INFO:usb_2 -- SETUP: bmrequesttype: 128, brequest: 6, wlength: 64, windex: 0, wvalue: 256
|
||||
INFO:usb_2 -- GET_DESCRIPTOR Device [length=64]
|
||||
INFO:usb_2 -- Goal reached; move to the next section
|
||||
USB: UsbReset @ Duration { secs: 0, nanos: 361145018 }
|
||||
USB: UsbEp0Setup @ Duration { secs: 0, nanos: 402465820 }
|
||||
SETUP: bmrequesttype: 0, brequest: 5, wlength: 0, windex: 0, wvalue: 10
|
||||
USB: UsbEp0Setup @ Duration { secs: 0, nanos: 404754637 }
|
||||
SETUP: bmrequesttype: 128, brequest: 6, wlength: 8, windex: 0, wvalue: 256
|
||||
GET_DESCRIPTOR Device [length=8]
|
||||
Goal reached; move to the next section
|
||||
`dk::exit()` called; exiting ...
|
||||
```
|
||||
|
||||
> Note: `wlength` / `length` can vary depending on the OS, USB port (USB 2.0 vs USB 3.0) or the presence of a USB hub so you may see a different value.
|
||||
|
|
|
@ -21,12 +21,15 @@ Also note that in the starter code the `idle` function has been modified. Pay at
|
|||
✅ Modify the program so that it prints the number of times the USB cable has been connected to the DK every time the cable is connected, as shown below.
|
||||
|
||||
``` console
|
||||
(..)
|
||||
INFO:resource -- on_power_event: cable connected 1 time
|
||||
(..)
|
||||
INFO:resource -- on_power_event: cable connected 2 times
|
||||
(..)
|
||||
INFO:resource -- on_power_event: cable connected 3 times
|
||||
USBDETECTED interrupt enabled
|
||||
idle: going to sleep
|
||||
on_power_event: cable connected 1 time
|
||||
idle: woke up
|
||||
idle: going to sleep
|
||||
on_power_event: cable connected 2 times
|
||||
idle: woke up
|
||||
idle: going to sleep
|
||||
on_power_event: cable connected 3 times
|
||||
```
|
||||
|
||||
You can find a solution to this exercise in the `resource-solution.rs` file.
|
|
@ -12,4 +12,4 @@ The other time related API exposed by the `dk` HAL is the `dk::uptime` function.
|
|||
|
||||
✅ Try changing the `Duration` value passed to `Timer.wait`. Try values larger than one second and smaller than one second. What values of `Duration` make the blinking imperceptible?
|
||||
|
||||
❗If you set the duration to below 100ms, try removing the `log::info!` command in the loop. Too much logging will fill the logging buffer and cause the loop to slow down, resulting in the blink frequency to reduce after a while.
|
||||
❗If you set the duration to below 2ms, try removing the `defmt::println!` command in the loop. Too much logging will fill the logging buffer and cause the loop to slow down, resulting in the blink frequency to reduce after a while.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
# USB Enumeration
|
||||
|
||||
Check this miro board for an [overview](https://miro.com/app/board/uXjVObcQhcc=/?invite_link_id=467100096053).
|
||||
|
||||
A USB device, like the nRF52840, can be one of these three states: the Default state, the Address state or the Configured state. After being powered the device will start in the Default state. The enumeration process will take the device from the Default state to the Address state. As a result of the enumeration process the device will be assigned an address, in the range `1..=127`, by the host.
|
||||
|
||||
The USB protocol is complex so we'll leave out many details and focus only on the concepts required to get enumeration and configuration working. There are also several USB specific terms so we recommend checking chapter 2, "Terms and Abbreviations", of the USB specification (linked at the bottom of this document) every now and then.
|
||||
|
|
|
@ -15,11 +15,11 @@ This code will panic because `USBRESET` is not implemented yet.
|
|||
✅ Go to `fn on_event`, line 39. In this section you'll need to implement the following USB events `USBRESET` and `EP0SETUP` so that your log output will look like this:
|
||||
|
||||
``` console
|
||||
INFO:usb_1 -- USB: UsbReset
|
||||
INFO:usb_1 -- returning to the Default state
|
||||
INFO:usb_1 -- USB: UsbEp0Setup
|
||||
INFO:usb_1 -- goal reached; move to the next section
|
||||
INFO:dk -- `dk::exit() called; exiting ...
|
||||
USBD initialized
|
||||
USB: UsbReset
|
||||
returning to the Default state
|
||||
USB: UsbEp0Setup
|
||||
goal reached; move to the next section
|
||||
```
|
||||
|
||||
## Help
|
||||
|
|
|
@ -28,7 +28,16 @@ $ cargo doc -p dk --open
|
|||
|
||||
✅ Check the API docs of the `Led` abstraction. Change the `led` program, so that the bottom two LEDs are turned on, and the top two are turned off.
|
||||
|
||||
✅ Uncomment the `log::set_max_level` line. This will make the logs more verbose; they will now include logs from the board initialization function (`dk::init`) and from the `Led` API.
|
||||
🔎 If you want to see logs from Led API of the `dk` Hardware Abstraction Layer, go to `boards/dk/Cargo.toml` and change the log level of the `dk` crate:
|
||||
|
||||
```diff
|
||||
# set defmt logging levels here
|
||||
default = [
|
||||
- "defmt-debug",
|
||||
+ "defmt-trace",
|
||||
# "dependency-a/defmt-trace",
|
||||
]
|
||||
```
|
||||
|
||||
Among the logs you'll find the line "I/O pins have been configured for digital output". At this point the electrical pins of the nRF52840 microcontroller have been configured to drive the 4 LEDs on the board.
|
||||
|
||||
|
|
Loading…
Reference in a new issue