diff options
| author | Dominik Boehi <[email protected]> | 2023-01-09 22:29:58 +0100 |
|---|---|---|
| committer | Dominik Boehi <[email protected]> | 2023-01-09 22:30:02 +0100 |
| commit | 0a27b6cedb52453123190671f294bbd34918e09a (patch) | |
| tree | 3888e0b352388ace235517ec97c4d3a88fd7fcfa /examples/nrf52840/src/bin | |
| parent | 401185b1d95a2519ee94e5d5654cc9325fe85eec (diff) | |
Rename examples/nrf to examples/nrf52840
Diffstat (limited to 'examples/nrf52840/src/bin')
51 files changed, 3355 insertions, 0 deletions
diff --git a/examples/nrf52840/src/bin/awaitable_timer.rs b/examples/nrf52840/src/bin/awaitable_timer.rs new file mode 100644 index 000000000..b32af236c --- /dev/null +++ b/examples/nrf52840/src/bin/awaitable_timer.rs | |||
| @@ -0,0 +1,26 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::timer::Timer; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_nrf::init(Default::default()); | ||
| 14 | let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); | ||
| 15 | // default frequency is 1MHz, so this triggers every second | ||
| 16 | t.cc(0).write(1_000_000); | ||
| 17 | // clear the timer value on cc[0] compare match | ||
| 18 | t.cc(0).short_compare_clear(); | ||
| 19 | t.start(); | ||
| 20 | |||
| 21 | loop { | ||
| 22 | // wait for compare match | ||
| 23 | t.cc(0).wait().await; | ||
| 24 | info!("hardware timer tick"); | ||
| 25 | } | ||
| 26 | } | ||
diff --git a/examples/nrf52840/src/bin/blinky.rs b/examples/nrf52840/src/bin/blinky.rs new file mode 100644 index 000000000..513f6cd82 --- /dev/null +++ b/examples/nrf52840/src/bin/blinky.rs | |||
| @@ -0,0 +1,21 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | ||
| 7 | use embassy_time::{Duration, Timer}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) { | ||
| 12 | let p = embassy_nrf::init(Default::default()); | ||
| 13 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); | ||
| 14 | |||
| 15 | loop { | ||
| 16 | led.set_high(); | ||
| 17 | Timer::after(Duration::from_millis(300)).await; | ||
| 18 | led.set_low(); | ||
| 19 | Timer::after(Duration::from_millis(300)).await; | ||
| 20 | } | ||
| 21 | } | ||
diff --git a/examples/nrf52840/src/bin/buffered_uart.rs b/examples/nrf52840/src/bin/buffered_uart.rs new file mode 100644 index 000000000..ea566f4b2 --- /dev/null +++ b/examples/nrf52840/src/bin/buffered_uart.rs | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::buffered_uarte::{BufferedUarte, State}; | ||
| 8 | use embassy_nrf::{interrupt, uarte}; | ||
| 9 | use embedded_io::asynch::{BufRead, Write}; | ||
| 10 | use futures::pin_mut; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) { | ||
| 15 | let p = embassy_nrf::init(Default::default()); | ||
| 16 | let mut config = uarte::Config::default(); | ||
| 17 | config.parity = uarte::Parity::EXCLUDED; | ||
| 18 | config.baudrate = uarte::Baudrate::BAUD115200; | ||
| 19 | |||
| 20 | let mut tx_buffer = [0u8; 4096]; | ||
| 21 | let mut rx_buffer = [0u8; 4096]; | ||
| 22 | |||
| 23 | let irq = interrupt::take!(UARTE0_UART0); | ||
| 24 | let mut state = State::new(); | ||
| 25 | // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536) | ||
| 26 | let u = BufferedUarte::new( | ||
| 27 | &mut state, | ||
| 28 | p.UARTE0, | ||
| 29 | p.TIMER0, | ||
| 30 | p.PPI_CH0, | ||
| 31 | p.PPI_CH1, | ||
| 32 | irq, | ||
| 33 | p.P0_08, | ||
| 34 | p.P0_06, | ||
| 35 | p.P0_07, | ||
| 36 | p.P0_05, | ||
| 37 | config, | ||
| 38 | &mut rx_buffer, | ||
| 39 | &mut tx_buffer, | ||
| 40 | ); | ||
| 41 | pin_mut!(u); | ||
| 42 | |||
| 43 | info!("uarte initialized!"); | ||
| 44 | |||
| 45 | unwrap!(u.write_all(b"Hello!\r\n").await); | ||
| 46 | info!("wrote hello in uart!"); | ||
| 47 | |||
| 48 | loop { | ||
| 49 | info!("reading..."); | ||
| 50 | let buf = unwrap!(u.fill_buf().await); | ||
| 51 | info!("read done, got {}", buf); | ||
| 52 | |||
| 53 | // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again | ||
| 54 | let n = buf.len(); | ||
| 55 | u.consume(n); | ||
| 56 | } | ||
| 57 | } | ||
diff --git a/examples/nrf52840/src/bin/channel.rs b/examples/nrf52840/src/bin/channel.rs new file mode 100644 index 000000000..d782a79e7 --- /dev/null +++ b/examples/nrf52840/src/bin/channel.rs | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::unwrap; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | ||
| 8 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 9 | use embassy_sync::channel::Channel; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | enum LedState { | ||
| 14 | On, | ||
| 15 | Off, | ||
| 16 | } | ||
| 17 | |||
| 18 | static CHANNEL: Channel<ThreadModeRawMutex, LedState, 1> = Channel::new(); | ||
| 19 | |||
| 20 | #[embassy_executor::task] | ||
| 21 | async fn my_task() { | ||
| 22 | loop { | ||
| 23 | CHANNEL.send(LedState::On).await; | ||
| 24 | Timer::after(Duration::from_secs(1)).await; | ||
| 25 | CHANNEL.send(LedState::Off).await; | ||
| 26 | Timer::after(Duration::from_secs(1)).await; | ||
| 27 | } | ||
| 28 | } | ||
| 29 | |||
| 30 | #[embassy_executor::main] | ||
| 31 | async fn main(spawner: Spawner) { | ||
| 32 | let p = embassy_nrf::init(Default::default()); | ||
| 33 | let mut led = Output::new(p.P0_13, Level::Low, OutputDrive::Standard); | ||
| 34 | |||
| 35 | unwrap!(spawner.spawn(my_task())); | ||
| 36 | |||
| 37 | loop { | ||
| 38 | match CHANNEL.recv().await { | ||
| 39 | LedState::On => led.set_high(), | ||
| 40 | LedState::Off => led.set_low(), | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
diff --git a/examples/nrf52840/src/bin/channel_sender_receiver.rs b/examples/nrf52840/src/bin/channel_sender_receiver.rs new file mode 100644 index 000000000..fcccdaed5 --- /dev/null +++ b/examples/nrf52840/src/bin/channel_sender_receiver.rs | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::unwrap; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive, Pin}; | ||
| 8 | use embassy_sync::blocking_mutex::raw::NoopRawMutex; | ||
| 9 | use embassy_sync::channel::{Channel, Receiver, Sender}; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use static_cell::StaticCell; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | enum LedState { | ||
| 15 | On, | ||
| 16 | Off, | ||
| 17 | } | ||
| 18 | |||
| 19 | static CHANNEL: StaticCell<Channel<NoopRawMutex, LedState, 1>> = StaticCell::new(); | ||
| 20 | |||
| 21 | #[embassy_executor::task] | ||
| 22 | async fn send_task(sender: Sender<'static, NoopRawMutex, LedState, 1>) { | ||
| 23 | loop { | ||
| 24 | sender.send(LedState::On).await; | ||
| 25 | Timer::after(Duration::from_secs(1)).await; | ||
| 26 | sender.send(LedState::Off).await; | ||
| 27 | Timer::after(Duration::from_secs(1)).await; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | #[embassy_executor::task] | ||
| 32 | async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedState, 1>) { | ||
| 33 | let mut led = Output::new(led, Level::Low, OutputDrive::Standard); | ||
| 34 | |||
| 35 | loop { | ||
| 36 | match receiver.recv().await { | ||
| 37 | LedState::On => led.set_high(), | ||
| 38 | LedState::Off => led.set_low(), | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | #[embassy_executor::main] | ||
| 44 | async fn main(spawner: Spawner) { | ||
| 45 | let p = embassy_nrf::init(Default::default()); | ||
| 46 | let channel = CHANNEL.init(Channel::new()); | ||
| 47 | |||
| 48 | unwrap!(spawner.spawn(send_task(channel.sender()))); | ||
| 49 | unwrap!(spawner.spawn(recv_task(p.P0_13.degrade(), channel.receiver()))); | ||
| 50 | } | ||
diff --git a/examples/nrf52840/src/bin/executor_fairness_test.rs b/examples/nrf52840/src/bin/executor_fairness_test.rs new file mode 100644 index 000000000..2a28f2763 --- /dev/null +++ b/examples/nrf52840/src/bin/executor_fairness_test.rs | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::future::poll_fn; | ||
| 6 | use core::task::Poll; | ||
| 7 | |||
| 8 | use defmt::{info, unwrap}; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_time::{Duration, Instant, Timer}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::task] | ||
| 14 | async fn run1() { | ||
| 15 | loop { | ||
| 16 | info!("DING DONG"); | ||
| 17 | Timer::after(Duration::from_ticks(16000)).await; | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | #[embassy_executor::task] | ||
| 22 | async fn run2() { | ||
| 23 | loop { | ||
| 24 | Timer::at(Instant::from_ticks(0)).await; | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | #[embassy_executor::task] | ||
| 29 | async fn run3() { | ||
| 30 | poll_fn(|cx| { | ||
| 31 | cx.waker().wake_by_ref(); | ||
| 32 | Poll::<()>::Pending | ||
| 33 | }) | ||
| 34 | .await; | ||
| 35 | } | ||
| 36 | |||
| 37 | #[embassy_executor::main] | ||
| 38 | async fn main(spawner: Spawner) { | ||
| 39 | let _p = embassy_nrf::init(Default::default()); | ||
| 40 | unwrap!(spawner.spawn(run1())); | ||
| 41 | unwrap!(spawner.spawn(run2())); | ||
| 42 | unwrap!(spawner.spawn(run3())); | ||
| 43 | } | ||
diff --git a/examples/nrf52840/src/bin/gpiote_channel.rs b/examples/nrf52840/src/bin/gpiote_channel.rs new file mode 100644 index 000000000..5bfd02465 --- /dev/null +++ b/examples/nrf52840/src/bin/gpiote_channel.rs | |||
| @@ -0,0 +1,66 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::gpio::{Input, Pull}; | ||
| 8 | use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_nrf::init(Default::default()); | ||
| 14 | info!("Starting!"); | ||
| 15 | |||
| 16 | let ch1 = InputChannel::new( | ||
| 17 | p.GPIOTE_CH0, | ||
| 18 | Input::new(p.P0_11, Pull::Up), | ||
| 19 | InputChannelPolarity::HiToLo, | ||
| 20 | ); | ||
| 21 | let ch2 = InputChannel::new( | ||
| 22 | p.GPIOTE_CH1, | ||
| 23 | Input::new(p.P0_12, Pull::Up), | ||
| 24 | InputChannelPolarity::LoToHi, | ||
| 25 | ); | ||
| 26 | let ch3 = InputChannel::new( | ||
| 27 | p.GPIOTE_CH2, | ||
| 28 | Input::new(p.P0_24, Pull::Up), | ||
| 29 | InputChannelPolarity::Toggle, | ||
| 30 | ); | ||
| 31 | let ch4 = InputChannel::new( | ||
| 32 | p.GPIOTE_CH3, | ||
| 33 | Input::new(p.P0_25, Pull::Up), | ||
| 34 | InputChannelPolarity::Toggle, | ||
| 35 | ); | ||
| 36 | |||
| 37 | let button1 = async { | ||
| 38 | loop { | ||
| 39 | ch1.wait().await; | ||
| 40 | info!("Button 1 pressed") | ||
| 41 | } | ||
| 42 | }; | ||
| 43 | |||
| 44 | let button2 = async { | ||
| 45 | loop { | ||
| 46 | ch2.wait().await; | ||
| 47 | info!("Button 2 released") | ||
| 48 | } | ||
| 49 | }; | ||
| 50 | |||
| 51 | let button3 = async { | ||
| 52 | loop { | ||
| 53 | ch3.wait().await; | ||
| 54 | info!("Button 3 toggled") | ||
| 55 | } | ||
| 56 | }; | ||
| 57 | |||
| 58 | let button4 = async { | ||
| 59 | loop { | ||
| 60 | ch4.wait().await; | ||
| 61 | info!("Button 4 toggled") | ||
| 62 | } | ||
| 63 | }; | ||
| 64 | |||
| 65 | futures::join!(button1, button2, button3, button4); | ||
| 66 | } | ||
diff --git a/examples/nrf52840/src/bin/gpiote_port.rs b/examples/nrf52840/src/bin/gpiote_port.rs new file mode 100644 index 000000000..0155d539e --- /dev/null +++ b/examples/nrf52840/src/bin/gpiote_port.rs | |||
| @@ -0,0 +1,34 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::gpio::{AnyPin, Input, Pin as _, Pull}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::task(pool_size = 4)] | ||
| 11 | async fn button_task(n: usize, mut pin: Input<'static, AnyPin>) { | ||
| 12 | loop { | ||
| 13 | pin.wait_for_low().await; | ||
| 14 | info!("Button {:?} pressed!", n); | ||
| 15 | pin.wait_for_high().await; | ||
| 16 | info!("Button {:?} released!", n); | ||
| 17 | } | ||
| 18 | } | ||
| 19 | |||
| 20 | #[embassy_executor::main] | ||
| 21 | async fn main(spawner: Spawner) { | ||
| 22 | let p = embassy_nrf::init(Default::default()); | ||
| 23 | info!("Starting!"); | ||
| 24 | |||
| 25 | let btn1 = Input::new(p.P0_11.degrade(), Pull::Up); | ||
| 26 | let btn2 = Input::new(p.P0_12.degrade(), Pull::Up); | ||
| 27 | let btn3 = Input::new(p.P0_24.degrade(), Pull::Up); | ||
| 28 | let btn4 = Input::new(p.P0_25.degrade(), Pull::Up); | ||
| 29 | |||
| 30 | unwrap!(spawner.spawn(button_task(1, btn1))); | ||
| 31 | unwrap!(spawner.spawn(button_task(2, btn2))); | ||
| 32 | unwrap!(spawner.spawn(button_task(3, btn3))); | ||
| 33 | unwrap!(spawner.spawn(button_task(4, btn4))); | ||
| 34 | } | ||
diff --git a/examples/nrf52840/src/bin/i2s_effect.rs b/examples/nrf52840/src/bin/i2s_effect.rs new file mode 100644 index 000000000..3cca005b1 --- /dev/null +++ b/examples/nrf52840/src/bin/i2s_effect.rs | |||
| @@ -0,0 +1,117 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::f32::consts::PI; | ||
| 6 | |||
| 7 | use defmt::{error, info}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::i2s::{self, Channels, Config, MasterClock, MultiBuffering, Sample as _, SampleWidth, I2S}; | ||
| 10 | use embassy_nrf::interrupt; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | type Sample = i16; | ||
| 14 | |||
| 15 | const NUM_BUFFERS: usize = 2; | ||
| 16 | const NUM_SAMPLES: usize = 4; | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_spawner: Spawner) { | ||
| 20 | let p = embassy_nrf::init(Default::default()); | ||
| 21 | |||
| 22 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 23 | |||
| 24 | let sample_rate = master_clock.sample_rate(); | ||
| 25 | info!("Sample rate: {}", sample_rate); | ||
| 26 | |||
| 27 | let config = Config::default() | ||
| 28 | .sample_width(SampleWidth::_16bit) | ||
| 29 | .channels(Channels::MonoLeft); | ||
| 30 | |||
| 31 | let irq = interrupt::take!(I2S); | ||
| 32 | let buffers_out = MultiBuffering::<Sample, NUM_BUFFERS, NUM_SAMPLES>::new(); | ||
| 33 | let buffers_in = MultiBuffering::<Sample, NUM_BUFFERS, NUM_SAMPLES>::new(); | ||
| 34 | let mut full_duplex_stream = I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).full_duplex( | ||
| 35 | p.P0_29, | ||
| 36 | p.P0_28, | ||
| 37 | buffers_out, | ||
| 38 | buffers_in, | ||
| 39 | ); | ||
| 40 | |||
| 41 | let mut modulator = SineOsc::new(); | ||
| 42 | modulator.set_frequency(8.0, 1.0 / sample_rate as f32); | ||
| 43 | modulator.set_amplitude(1.0); | ||
| 44 | |||
| 45 | full_duplex_stream.start().await.expect("I2S Start"); | ||
| 46 | |||
| 47 | loop { | ||
| 48 | let (buff_out, buff_in) = full_duplex_stream.buffers(); | ||
| 49 | for i in 0..NUM_SAMPLES { | ||
| 50 | let modulation = (Sample::SCALE as f32 * bipolar_to_unipolar(modulator.generate())) as Sample; | ||
| 51 | buff_out[i] = buff_in[i] * modulation; | ||
| 52 | } | ||
| 53 | |||
| 54 | if let Err(err) = full_duplex_stream.send_and_receive().await { | ||
| 55 | error!("{}", err); | ||
| 56 | } | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | struct SineOsc { | ||
| 61 | amplitude: f32, | ||
| 62 | modulo: f32, | ||
| 63 | phase_inc: f32, | ||
| 64 | } | ||
| 65 | |||
| 66 | impl SineOsc { | ||
| 67 | const B: f32 = 4.0 / PI; | ||
| 68 | const C: f32 = -4.0 / (PI * PI); | ||
| 69 | const P: f32 = 0.225; | ||
| 70 | |||
| 71 | pub fn new() -> Self { | ||
| 72 | Self { | ||
| 73 | amplitude: 1.0, | ||
| 74 | modulo: 0.0, | ||
| 75 | phase_inc: 0.0, | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { | ||
| 80 | self.phase_inc = freq * inv_sample_rate; | ||
| 81 | } | ||
| 82 | |||
| 83 | pub fn set_amplitude(&mut self, amplitude: f32) { | ||
| 84 | self.amplitude = amplitude; | ||
| 85 | } | ||
| 86 | |||
| 87 | pub fn generate(&mut self) -> f32 { | ||
| 88 | let signal = self.parabolic_sin(self.modulo); | ||
| 89 | self.modulo += self.phase_inc; | ||
| 90 | if self.modulo < 0.0 { | ||
| 91 | self.modulo += 1.0; | ||
| 92 | } else if self.modulo > 1.0 { | ||
| 93 | self.modulo -= 1.0; | ||
| 94 | } | ||
| 95 | signal * self.amplitude | ||
| 96 | } | ||
| 97 | |||
| 98 | fn parabolic_sin(&mut self, modulo: f32) -> f32 { | ||
| 99 | let angle = PI - modulo * 2.0 * PI; | ||
| 100 | let y = Self::B * angle + Self::C * angle * abs(angle); | ||
| 101 | Self::P * (y * abs(y) - y) + y | ||
| 102 | } | ||
| 103 | } | ||
| 104 | |||
| 105 | #[inline] | ||
| 106 | fn abs(value: f32) -> f32 { | ||
| 107 | if value < 0.0 { | ||
| 108 | -value | ||
| 109 | } else { | ||
| 110 | value | ||
| 111 | } | ||
| 112 | } | ||
| 113 | |||
| 114 | #[inline] | ||
| 115 | fn bipolar_to_unipolar(value: f32) -> f32 { | ||
| 116 | (value + 1.0) / 2.0 | ||
| 117 | } | ||
diff --git a/examples/nrf52840/src/bin/i2s_monitor.rs b/examples/nrf52840/src/bin/i2s_monitor.rs new file mode 100644 index 000000000..48eb7d581 --- /dev/null +++ b/examples/nrf52840/src/bin/i2s_monitor.rs | |||
| @@ -0,0 +1,115 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{debug, error, info}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; | ||
| 8 | use embassy_nrf::interrupt; | ||
| 9 | use embassy_nrf::pwm::{Prescaler, SimplePwm}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | type Sample = i16; | ||
| 13 | |||
| 14 | const NUM_SAMPLES: usize = 500; | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(_spawner: Spawner) { | ||
| 18 | let p = embassy_nrf::init(Default::default()); | ||
| 19 | |||
| 20 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 21 | |||
| 22 | let sample_rate = master_clock.sample_rate(); | ||
| 23 | info!("Sample rate: {}", sample_rate); | ||
| 24 | |||
| 25 | let config = Config::default() | ||
| 26 | .sample_width(SampleWidth::_16bit) | ||
| 27 | .channels(Channels::MonoLeft); | ||
| 28 | |||
| 29 | let irq = interrupt::take!(I2S); | ||
| 30 | let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new(); | ||
| 31 | let mut input_stream = | ||
| 32 | I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).input(p.P0_29, buffers); | ||
| 33 | |||
| 34 | // Configure the PWM to use the pins corresponding to the RGB leds | ||
| 35 | let mut pwm = SimplePwm::new_3ch(p.PWM0, p.P0_23, p.P0_22, p.P0_24); | ||
| 36 | pwm.set_prescaler(Prescaler::Div1); | ||
| 37 | pwm.set_max_duty(255); | ||
| 38 | |||
| 39 | let mut rms_online = RmsOnline::<NUM_SAMPLES>::default(); | ||
| 40 | |||
| 41 | input_stream.start().await.expect("I2S Start"); | ||
| 42 | |||
| 43 | loop { | ||
| 44 | let rms = rms_online.process(input_stream.buffer()); | ||
| 45 | let rgb = rgb_from_rms(rms); | ||
| 46 | |||
| 47 | debug!("RMS: {}, RGB: {:?}", rms, rgb); | ||
| 48 | for i in 0..3 { | ||
| 49 | pwm.set_duty(i, rgb[i].into()); | ||
| 50 | } | ||
| 51 | |||
| 52 | if let Err(err) = input_stream.receive().await { | ||
| 53 | error!("{}", err); | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | |||
| 58 | /// RMS from 0.0 until 0.75 will give green with a proportional intensity | ||
| 59 | /// RMS from 0.75 until 0.9 will give a blend between orange and red proportionally to the intensity | ||
| 60 | /// RMS above 0.9 will give a red with a proportional intensity | ||
| 61 | fn rgb_from_rms(rms: f32) -> [u8; 3] { | ||
| 62 | if rms < 0.75 { | ||
| 63 | let intensity = rms / 0.75; | ||
| 64 | [0, (intensity * 165.0) as u8, 0] | ||
| 65 | } else if rms < 0.9 { | ||
| 66 | let intensity = (rms - 0.75) / 0.15; | ||
| 67 | [200, 165 - (165.0 * intensity) as u8, 0] | ||
| 68 | } else { | ||
| 69 | let intensity = (rms - 0.9) / 0.1; | ||
| 70 | [200 + (55.0 * intensity) as u8, 0, 0] | ||
| 71 | } | ||
| 72 | } | ||
| 73 | |||
| 74 | pub struct RmsOnline<const N: usize> { | ||
| 75 | pub squares: [f32; N], | ||
| 76 | pub head: usize, | ||
| 77 | } | ||
| 78 | |||
| 79 | impl<const N: usize> Default for RmsOnline<N> { | ||
| 80 | fn default() -> Self { | ||
| 81 | RmsOnline { | ||
| 82 | squares: [0.0; N], | ||
| 83 | head: 0, | ||
| 84 | } | ||
| 85 | } | ||
| 86 | } | ||
| 87 | |||
| 88 | impl<const N: usize> RmsOnline<N> { | ||
| 89 | pub fn reset(&mut self) { | ||
| 90 | self.squares = [0.0; N]; | ||
| 91 | self.head = 0; | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn process(&mut self, buf: &[Sample]) -> f32 { | ||
| 95 | buf.iter() | ||
| 96 | .for_each(|sample| self.push(*sample as f32 / Sample::SCALE as f32)); | ||
| 97 | |||
| 98 | let sum_of_squares = self.squares.iter().fold(0.0, |acc, v| acc + *v); | ||
| 99 | Self::approx_sqrt(sum_of_squares / N as f32) | ||
| 100 | } | ||
| 101 | |||
| 102 | pub fn push(&mut self, signal: f32) { | ||
| 103 | let square = signal * signal; | ||
| 104 | self.squares[self.head] = square; | ||
| 105 | self.head = (self.head + 1) % N; | ||
| 106 | } | ||
| 107 | |||
| 108 | /// Approximated sqrt taken from [micromath] | ||
| 109 | /// | ||
| 110 | /// [micromath]: https://docs.rs/micromath/latest/src/micromath/float/sqrt.rs.html#11-17 | ||
| 111 | /// | ||
| 112 | fn approx_sqrt(value: f32) -> f32 { | ||
| 113 | f32::from_bits((value.to_bits() + 0x3f80_0000) >> 1) | ||
| 114 | } | ||
| 115 | } | ||
diff --git a/examples/nrf52840/src/bin/i2s_waveform.rs b/examples/nrf52840/src/bin/i2s_waveform.rs new file mode 100644 index 000000000..1b0e8ebc8 --- /dev/null +++ b/examples/nrf52840/src/bin/i2s_waveform.rs | |||
| @@ -0,0 +1,151 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::f32::consts::PI; | ||
| 6 | |||
| 7 | use defmt::{error, info}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::i2s::{self, Channels, Config, DoubleBuffering, MasterClock, Sample as _, SampleWidth, I2S}; | ||
| 10 | use embassy_nrf::interrupt; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | type Sample = i16; | ||
| 14 | |||
| 15 | const NUM_SAMPLES: usize = 50; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_nrf::init(Default::default()); | ||
| 20 | |||
| 21 | let master_clock: MasterClock = i2s::ExactSampleRate::_50000.into(); | ||
| 22 | |||
| 23 | let sample_rate = master_clock.sample_rate(); | ||
| 24 | info!("Sample rate: {}", sample_rate); | ||
| 25 | |||
| 26 | let config = Config::default() | ||
| 27 | .sample_width(SampleWidth::_16bit) | ||
| 28 | .channels(Channels::MonoLeft); | ||
| 29 | |||
| 30 | let irq = interrupt::take!(I2S); | ||
| 31 | let buffers = DoubleBuffering::<Sample, NUM_SAMPLES>::new(); | ||
| 32 | let mut output_stream = | ||
| 33 | I2S::master(p.I2S, irq, p.P0_25, p.P0_26, p.P0_27, master_clock, config).output(p.P0_28, buffers); | ||
| 34 | |||
| 35 | let mut waveform = Waveform::new(1.0 / sample_rate as f32); | ||
| 36 | |||
| 37 | waveform.process(output_stream.buffer()); | ||
| 38 | |||
| 39 | output_stream.start().await.expect("I2S Start"); | ||
| 40 | |||
| 41 | loop { | ||
| 42 | waveform.process(output_stream.buffer()); | ||
| 43 | |||
| 44 | if let Err(err) = output_stream.send().await { | ||
| 45 | error!("{}", err); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | } | ||
| 49 | |||
| 50 | struct Waveform { | ||
| 51 | inv_sample_rate: f32, | ||
| 52 | carrier: SineOsc, | ||
| 53 | freq_mod: SineOsc, | ||
| 54 | amp_mod: SineOsc, | ||
| 55 | } | ||
| 56 | |||
| 57 | impl Waveform { | ||
| 58 | fn new(inv_sample_rate: f32) -> Self { | ||
| 59 | let mut carrier = SineOsc::new(); | ||
| 60 | carrier.set_frequency(110.0, inv_sample_rate); | ||
| 61 | |||
| 62 | let mut freq_mod = SineOsc::new(); | ||
| 63 | freq_mod.set_frequency(1.0, inv_sample_rate); | ||
| 64 | freq_mod.set_amplitude(1.0); | ||
| 65 | |||
| 66 | let mut amp_mod = SineOsc::new(); | ||
| 67 | amp_mod.set_frequency(16.0, inv_sample_rate); | ||
| 68 | amp_mod.set_amplitude(0.5); | ||
| 69 | |||
| 70 | Self { | ||
| 71 | inv_sample_rate, | ||
| 72 | carrier, | ||
| 73 | freq_mod, | ||
| 74 | amp_mod, | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | fn process(&mut self, buf: &mut [Sample]) { | ||
| 79 | for sample in buf.chunks_mut(1) { | ||
| 80 | let freq_modulation = bipolar_to_unipolar(self.freq_mod.generate()); | ||
| 81 | self.carrier | ||
| 82 | .set_frequency(110.0 + 440.0 * freq_modulation, self.inv_sample_rate); | ||
| 83 | |||
| 84 | let amp_modulation = bipolar_to_unipolar(self.amp_mod.generate()); | ||
| 85 | self.carrier.set_amplitude(amp_modulation); | ||
| 86 | |||
| 87 | let signal = self.carrier.generate(); | ||
| 88 | |||
| 89 | sample[0] = (Sample::SCALE as f32 * signal) as Sample; | ||
| 90 | } | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | struct SineOsc { | ||
| 95 | amplitude: f32, | ||
| 96 | modulo: f32, | ||
| 97 | phase_inc: f32, | ||
| 98 | } | ||
| 99 | |||
| 100 | impl SineOsc { | ||
| 101 | const B: f32 = 4.0 / PI; | ||
| 102 | const C: f32 = -4.0 / (PI * PI); | ||
| 103 | const P: f32 = 0.225; | ||
| 104 | |||
| 105 | pub fn new() -> Self { | ||
| 106 | Self { | ||
| 107 | amplitude: 1.0, | ||
| 108 | modulo: 0.0, | ||
| 109 | phase_inc: 0.0, | ||
| 110 | } | ||
| 111 | } | ||
| 112 | |||
| 113 | pub fn set_frequency(&mut self, freq: f32, inv_sample_rate: f32) { | ||
| 114 | self.phase_inc = freq * inv_sample_rate; | ||
| 115 | } | ||
| 116 | |||
| 117 | pub fn set_amplitude(&mut self, amplitude: f32) { | ||
| 118 | self.amplitude = amplitude; | ||
| 119 | } | ||
| 120 | |||
| 121 | pub fn generate(&mut self) -> f32 { | ||
| 122 | let signal = self.parabolic_sin(self.modulo); | ||
| 123 | self.modulo += self.phase_inc; | ||
| 124 | if self.modulo < 0.0 { | ||
| 125 | self.modulo += 1.0; | ||
| 126 | } else if self.modulo > 1.0 { | ||
| 127 | self.modulo -= 1.0; | ||
| 128 | } | ||
| 129 | signal * self.amplitude | ||
| 130 | } | ||
| 131 | |||
| 132 | fn parabolic_sin(&mut self, modulo: f32) -> f32 { | ||
| 133 | let angle = PI - modulo * 2.0 * PI; | ||
| 134 | let y = Self::B * angle + Self::C * angle * abs(angle); | ||
| 135 | Self::P * (y * abs(y) - y) + y | ||
| 136 | } | ||
| 137 | } | ||
| 138 | |||
| 139 | #[inline] | ||
| 140 | fn abs(value: f32) -> f32 { | ||
| 141 | if value < 0.0 { | ||
| 142 | -value | ||
| 143 | } else { | ||
| 144 | value | ||
| 145 | } | ||
| 146 | } | ||
| 147 | |||
| 148 | #[inline] | ||
| 149 | fn bipolar_to_unipolar(value: f32) -> f32 { | ||
| 150 | (value + 1.0) / 2.0 | ||
| 151 | } | ||
diff --git a/examples/nrf52840/src/bin/lora_p2p_report.rs b/examples/nrf52840/src/bin/lora_p2p_report.rs new file mode 100644 index 000000000..d512b83f6 --- /dev/null +++ b/examples/nrf52840/src/bin/lora_p2p_report.rs | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||
| 2 | //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||
| 3 | //! It demonstates LORA P2P functionality in conjunction with example lora_p2p_sense.rs. | ||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![allow(dead_code)] | ||
| 8 | #![feature(type_alias_impl_trait)] | ||
| 9 | |||
| 10 | use defmt::*; | ||
| 11 | use embassy_executor::Spawner; | ||
| 12 | use embassy_lora::sx126x::*; | ||
| 13 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||
| 14 | use embassy_nrf::{interrupt, spim}; | ||
| 15 | use embassy_time::{Duration, Timer}; | ||
| 16 | use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor}; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) { | ||
| 21 | let p = embassy_nrf::init(Default::default()); | ||
| 22 | let mut spi_config = spim::Config::default(); | ||
| 23 | spi_config.frequency = spim::Frequency::M16; | ||
| 24 | |||
| 25 | let mut radio = { | ||
| 26 | let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); | ||
| 27 | let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||
| 28 | |||
| 29 | let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||
| 30 | let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||
| 31 | let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||
| 32 | let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||
| 33 | let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||
| 34 | let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||
| 35 | |||
| 36 | match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { | ||
| 37 | Ok(r) => r, | ||
| 38 | Err(err) => { | ||
| 39 | info!("Sx126xRadio error = {}", err); | ||
| 40 | return; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | }; | ||
| 44 | |||
| 45 | let mut debug_indicator = Output::new(p.P1_03, Level::Low, OutputDrive::Standard); | ||
| 46 | let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||
| 47 | |||
| 48 | start_indicator.set_high(); | ||
| 49 | Timer::after(Duration::from_secs(5)).await; | ||
| 50 | start_indicator.set_low(); | ||
| 51 | |||
| 52 | loop { | ||
| 53 | let rf_config = RfConfig { | ||
| 54 | frequency: 903900000, // channel in Hz | ||
| 55 | bandwidth: Bandwidth::_250KHz, | ||
| 56 | spreading_factor: SpreadingFactor::_10, | ||
| 57 | coding_rate: CodingRate::_4_8, | ||
| 58 | }; | ||
| 59 | |||
| 60 | let mut buffer = [00u8; 100]; | ||
| 61 | |||
| 62 | // P2P receive | ||
| 63 | match radio.rx(rf_config, &mut buffer).await { | ||
| 64 | Ok((buffer_len, rx_quality)) => info!( | ||
| 65 | "RX received = {:?} with length = {} rssi = {} snr = {}", | ||
| 66 | &buffer[0..buffer_len], | ||
| 67 | buffer_len, | ||
| 68 | rx_quality.rssi(), | ||
| 69 | rx_quality.snr() | ||
| 70 | ), | ||
| 71 | Err(err) => info!("RX error = {}", err), | ||
| 72 | } | ||
| 73 | |||
| 74 | debug_indicator.set_high(); | ||
| 75 | Timer::after(Duration::from_secs(2)).await; | ||
| 76 | debug_indicator.set_low(); | ||
| 77 | } | ||
| 78 | } | ||
diff --git a/examples/nrf52840/src/bin/lora_p2p_sense.rs b/examples/nrf52840/src/bin/lora_p2p_sense.rs new file mode 100644 index 000000000..b9768874b --- /dev/null +++ b/examples/nrf52840/src/bin/lora_p2p_sense.rs | |||
| @@ -0,0 +1,125 @@ | |||
| 1 | //! This example runs on the RAK4631 WisBlock, which has an nRF52840 MCU and Semtech Sx126x radio. | ||
| 2 | //! Other nrf/sx126x combinations may work with appropriate pin modifications. | ||
| 3 | //! It demonstates LORA P2P functionality in conjunction with example lora_p2p_report.rs. | ||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![macro_use] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | #![feature(alloc_error_handler)] | ||
| 9 | #![allow(incomplete_features)] | ||
| 10 | |||
| 11 | use defmt::*; | ||
| 12 | use embassy_executor::Spawner; | ||
| 13 | use embassy_lora::sx126x::*; | ||
| 14 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pin as _, Pull}; | ||
| 15 | use embassy_nrf::{interrupt, spim}; | ||
| 16 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 17 | use embassy_sync::pubsub::{PubSubChannel, Publisher}; | ||
| 18 | use embassy_time::{Duration, Timer}; | ||
| 19 | use lorawan_device::async_device::radio::{Bandwidth, CodingRate, PhyRxTx, RfConfig, SpreadingFactor, TxConfig}; | ||
| 20 | use {defmt_rtt as _, panic_probe as _, panic_probe as _}; | ||
| 21 | |||
| 22 | // Message bus: queue of 2, 1 subscriber (Lora P2P), 2 publishers (temperature, motion detection) | ||
| 23 | static MESSAGE_BUS: PubSubChannel<CriticalSectionRawMutex, Message, 2, 1, 2> = PubSubChannel::new(); | ||
| 24 | |||
| 25 | #[derive(Clone, defmt::Format)] | ||
| 26 | enum Message { | ||
| 27 | Temperature(i32), | ||
| 28 | MotionDetected, | ||
| 29 | } | ||
| 30 | |||
| 31 | #[embassy_executor::task] | ||
| 32 | async fn temperature_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { | ||
| 33 | // Publish a fake temperature every 43 seconds, minimizing LORA traffic. | ||
| 34 | loop { | ||
| 35 | Timer::after(Duration::from_secs(43)).await; | ||
| 36 | publisher.publish(Message::Temperature(9)).await; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | #[embassy_executor::task] | ||
| 41 | async fn motion_detection_task(publisher: Publisher<'static, CriticalSectionRawMutex, Message, 2, 1, 2>) { | ||
| 42 | // Publish a fake motion detection every 79 seconds, minimizing LORA traffic. | ||
| 43 | loop { | ||
| 44 | Timer::after(Duration::from_secs(79)).await; | ||
| 45 | publisher.publish(Message::MotionDetected).await; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | #[embassy_executor::main] | ||
| 50 | async fn main(spawner: Spawner) { | ||
| 51 | let p = embassy_nrf::init(Default::default()); | ||
| 52 | // set up to funnel temperature and motion detection events to the Lora Tx task | ||
| 53 | let mut lora_tx_subscriber = unwrap!(MESSAGE_BUS.subscriber()); | ||
| 54 | let temperature_publisher = unwrap!(MESSAGE_BUS.publisher()); | ||
| 55 | let motion_detection_publisher = unwrap!(MESSAGE_BUS.publisher()); | ||
| 56 | |||
| 57 | let mut spi_config = spim::Config::default(); | ||
| 58 | spi_config.frequency = spim::Frequency::M16; | ||
| 59 | |||
| 60 | let mut radio = { | ||
| 61 | let irq = interrupt::take!(SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1); | ||
| 62 | let spim = spim::Spim::new(p.TWISPI1, irq, p.P1_11, p.P1_13, p.P1_12, spi_config); | ||
| 63 | |||
| 64 | let cs = Output::new(p.P1_10.degrade(), Level::High, OutputDrive::Standard); | ||
| 65 | let reset = Output::new(p.P1_06.degrade(), Level::High, OutputDrive::Standard); | ||
| 66 | let dio1 = Input::new(p.P1_15.degrade(), Pull::Down); | ||
| 67 | let busy = Input::new(p.P1_14.degrade(), Pull::Down); | ||
| 68 | let antenna_rx = Output::new(p.P1_05.degrade(), Level::Low, OutputDrive::Standard); | ||
| 69 | let antenna_tx = Output::new(p.P1_07.degrade(), Level::Low, OutputDrive::Standard); | ||
| 70 | |||
| 71 | match Sx126xRadio::new(spim, cs, reset, antenna_rx, antenna_tx, dio1, busy, false).await { | ||
| 72 | Ok(r) => r, | ||
| 73 | Err(err) => { | ||
| 74 | info!("Sx126xRadio error = {}", err); | ||
| 75 | return; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | }; | ||
| 79 | |||
| 80 | let mut start_indicator = Output::new(p.P1_04, Level::Low, OutputDrive::Standard); | ||
| 81 | |||
| 82 | start_indicator.set_high(); | ||
| 83 | Timer::after(Duration::from_secs(5)).await; | ||
| 84 | start_indicator.set_low(); | ||
| 85 | |||
| 86 | match radio.lora.sleep().await { | ||
| 87 | Ok(()) => info!("Sleep successful"), | ||
| 88 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 89 | } | ||
| 90 | |||
| 91 | unwrap!(spawner.spawn(temperature_task(temperature_publisher))); | ||
| 92 | unwrap!(spawner.spawn(motion_detection_task(motion_detection_publisher))); | ||
| 93 | |||
| 94 | loop { | ||
| 95 | let message = lora_tx_subscriber.next_message_pure().await; | ||
| 96 | |||
| 97 | let tx_config = TxConfig { | ||
| 98 | // 11 byte maximum payload for Bandwidth 125 and SF 10 | ||
| 99 | pw: 10, // up to 20 | ||
| 100 | rf: RfConfig { | ||
| 101 | frequency: 903900000, // channel in Hz, not MHz | ||
| 102 | bandwidth: Bandwidth::_250KHz, | ||
| 103 | spreading_factor: SpreadingFactor::_10, | ||
| 104 | coding_rate: CodingRate::_4_8, | ||
| 105 | }, | ||
| 106 | }; | ||
| 107 | |||
| 108 | let mut buffer = [0x00u8]; | ||
| 109 | match message { | ||
| 110 | Message::Temperature(temperature) => buffer[0] = temperature as u8, | ||
| 111 | Message::MotionDetected => buffer[0] = 0x01u8, | ||
| 112 | }; | ||
| 113 | |||
| 114 | // unencrypted | ||
| 115 | match radio.tx(tx_config, &buffer).await { | ||
| 116 | Ok(ret_val) => info!("TX ret_val = {}", ret_val), | ||
| 117 | Err(err) => info!("TX error = {}", err), | ||
| 118 | } | ||
| 119 | |||
| 120 | match radio.lora.sleep().await { | ||
| 121 | Ok(()) => info!("Sleep successful"), | ||
| 122 | Err(err) => info!("Sleep unsuccessful = {}", err), | ||
| 123 | } | ||
| 124 | } | ||
| 125 | } | ||
diff --git a/examples/nrf52840/src/bin/manually_create_executor.rs b/examples/nrf52840/src/bin/manually_create_executor.rs new file mode 100644 index 000000000..12ce660f9 --- /dev/null +++ b/examples/nrf52840/src/bin/manually_create_executor.rs | |||
| @@ -0,0 +1,49 @@ | |||
| 1 | // This example showcases how to manually create an executor. | ||
| 2 | // This is what the #[embassy::main] macro does behind the scenes. | ||
| 3 | |||
| 4 | #![no_std] | ||
| 5 | #![no_main] | ||
| 6 | #![feature(type_alias_impl_trait)] | ||
| 7 | |||
| 8 | use cortex_m_rt::entry; | ||
| 9 | use defmt::{info, unwrap}; | ||
| 10 | use embassy_executor::Executor; | ||
| 11 | use embassy_time::{Duration, Timer}; | ||
| 12 | use static_cell::StaticCell; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::task] | ||
| 16 | async fn run1() { | ||
| 17 | loop { | ||
| 18 | info!("BIG INFREQUENT TICK"); | ||
| 19 | Timer::after(Duration::from_ticks(64000)).await; | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | #[embassy_executor::task] | ||
| 24 | async fn run2() { | ||
| 25 | loop { | ||
| 26 | info!("tick"); | ||
| 27 | Timer::after(Duration::from_ticks(13000)).await; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 32 | |||
| 33 | #[entry] | ||
| 34 | fn main() -> ! { | ||
| 35 | info!("Hello World!"); | ||
| 36 | |||
| 37 | let _p = embassy_nrf::init(Default::default()); | ||
| 38 | |||
| 39 | // Create the executor and put it in a StaticCell, because `run` needs `&'static mut Executor`. | ||
| 40 | let executor = EXECUTOR.init(Executor::new()); | ||
| 41 | |||
| 42 | // Run it. | ||
| 43 | // `run` calls the closure then runs the executor forever. It never returns. | ||
| 44 | executor.run(|spawner| { | ||
| 45 | // Here we get access to a spawner to spawn the initial tasks. | ||
| 46 | unwrap!(spawner.spawn(run1())); | ||
| 47 | unwrap!(spawner.spawn(run2())); | ||
| 48 | }); | ||
| 49 | } | ||
diff --git a/examples/nrf52840/src/bin/multiprio.rs b/examples/nrf52840/src/bin/multiprio.rs new file mode 100644 index 000000000..25806ae48 --- /dev/null +++ b/examples/nrf52840/src/bin/multiprio.rs | |||
| @@ -0,0 +1,140 @@ | |||
| 1 | //! This example showcases how to create multiple Executor instances to run tasks at | ||
| 2 | //! different priority levels. | ||
| 3 | //! | ||
| 4 | //! Low priority executor runs in thread mode (not interrupt), and uses `sev` for signaling | ||
| 5 | //! there's work in the queue, and `wfe` for waiting for work. | ||
| 6 | //! | ||
| 7 | //! Medium and high priority executors run in two interrupts with different priorities. | ||
| 8 | //! Signaling work is done by pending the interrupt. No "waiting" needs to be done explicitly, since | ||
| 9 | //! when there's work the interrupt will trigger and run the executor. | ||
| 10 | //! | ||
| 11 | //! Sample output below. Note that high priority ticks can interrupt everything else, and | ||
| 12 | //! medium priority computations can interrupt low priority computations, making them to appear | ||
| 13 | //! to take significantly longer time. | ||
| 14 | //! | ||
| 15 | //! ```not_rust | ||
| 16 | //! [med] Starting long computation | ||
| 17 | //! [med] done in 992 ms | ||
| 18 | //! [high] tick! | ||
| 19 | //! [low] Starting long computation | ||
| 20 | //! [med] Starting long computation | ||
| 21 | //! [high] tick! | ||
| 22 | //! [high] tick! | ||
| 23 | //! [med] done in 993 ms | ||
| 24 | //! [med] Starting long computation | ||
| 25 | //! [high] tick! | ||
| 26 | //! [high] tick! | ||
| 27 | //! [med] done in 993 ms | ||
| 28 | //! [low] done in 3972 ms | ||
| 29 | //! [med] Starting long computation | ||
| 30 | //! [high] tick! | ||
| 31 | //! [high] tick! | ||
| 32 | //! [med] done in 993 ms | ||
| 33 | //! ``` | ||
| 34 | //! | ||
| 35 | //! For comparison, try changing the code so all 3 tasks get spawned on the low priority executor. | ||
| 36 | //! You will get an output like the following. Note that no computation is ever interrupted. | ||
| 37 | //! | ||
| 38 | //! ```not_rust | ||
| 39 | //! [high] tick! | ||
| 40 | //! [med] Starting long computation | ||
| 41 | //! [med] done in 496 ms | ||
| 42 | //! [low] Starting long computation | ||
| 43 | //! [low] done in 992 ms | ||
| 44 | //! [med] Starting long computation | ||
| 45 | //! [med] done in 496 ms | ||
| 46 | //! [high] tick! | ||
| 47 | //! [low] Starting long computation | ||
| 48 | //! [low] done in 992 ms | ||
| 49 | //! [high] tick! | ||
| 50 | //! [med] Starting long computation | ||
| 51 | //! [med] done in 496 ms | ||
| 52 | //! [high] tick! | ||
| 53 | //! ``` | ||
| 54 | //! | ||
| 55 | |||
| 56 | #![no_std] | ||
| 57 | #![no_main] | ||
| 58 | #![feature(type_alias_impl_trait)] | ||
| 59 | |||
| 60 | use cortex_m_rt::entry; | ||
| 61 | use defmt::{info, unwrap}; | ||
| 62 | use embassy_nrf::executor::{Executor, InterruptExecutor}; | ||
| 63 | use embassy_nrf::interrupt; | ||
| 64 | use embassy_nrf::interrupt::InterruptExt; | ||
| 65 | use embassy_time::{Duration, Instant, Timer}; | ||
| 66 | use static_cell::StaticCell; | ||
| 67 | use {defmt_rtt as _, panic_probe as _}; | ||
| 68 | |||
| 69 | #[embassy_executor::task] | ||
| 70 | async fn run_high() { | ||
| 71 | loop { | ||
| 72 | info!(" [high] tick!"); | ||
| 73 | Timer::after(Duration::from_ticks(27374)).await; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | #[embassy_executor::task] | ||
| 78 | async fn run_med() { | ||
| 79 | loop { | ||
| 80 | let start = Instant::now(); | ||
| 81 | info!(" [med] Starting long computation"); | ||
| 82 | |||
| 83 | // Spin-wait to simulate a long CPU computation | ||
| 84 | cortex_m::asm::delay(32_000_000); // ~1 second | ||
| 85 | |||
| 86 | let end = Instant::now(); | ||
| 87 | let ms = end.duration_since(start).as_ticks() / 33; | ||
| 88 | info!(" [med] done in {} ms", ms); | ||
| 89 | |||
| 90 | Timer::after(Duration::from_ticks(23421)).await; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | #[embassy_executor::task] | ||
| 95 | async fn run_low() { | ||
| 96 | loop { | ||
| 97 | let start = Instant::now(); | ||
| 98 | info!("[low] Starting long computation"); | ||
| 99 | |||
| 100 | // Spin-wait to simulate a long CPU computation | ||
| 101 | cortex_m::asm::delay(64_000_000); // ~2 seconds | ||
| 102 | |||
| 103 | let end = Instant::now(); | ||
| 104 | let ms = end.duration_since(start).as_ticks() / 33; | ||
| 105 | info!("[low] done in {} ms", ms); | ||
| 106 | |||
| 107 | Timer::after(Duration::from_ticks(32983)).await; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | static EXECUTOR_HIGH: StaticCell<InterruptExecutor<interrupt::SWI1_EGU1>> = StaticCell::new(); | ||
| 112 | static EXECUTOR_MED: StaticCell<InterruptExecutor<interrupt::SWI0_EGU0>> = StaticCell::new(); | ||
| 113 | static EXECUTOR_LOW: StaticCell<Executor> = StaticCell::new(); | ||
| 114 | |||
| 115 | #[entry] | ||
| 116 | fn main() -> ! { | ||
| 117 | info!("Hello World!"); | ||
| 118 | |||
| 119 | let _p = embassy_nrf::init(Default::default()); | ||
| 120 | |||
| 121 | // High-priority executor: SWI1_EGU1, priority level 6 | ||
| 122 | let irq = interrupt::take!(SWI1_EGU1); | ||
| 123 | irq.set_priority(interrupt::Priority::P6); | ||
| 124 | let executor = EXECUTOR_HIGH.init(InterruptExecutor::new(irq)); | ||
| 125 | let spawner = executor.start(); | ||
| 126 | unwrap!(spawner.spawn(run_high())); | ||
| 127 | |||
| 128 | // Medium-priority executor: SWI0_EGU0, priority level 7 | ||
| 129 | let irq = interrupt::take!(SWI0_EGU0); | ||
| 130 | irq.set_priority(interrupt::Priority::P7); | ||
| 131 | let executor = EXECUTOR_MED.init(InterruptExecutor::new(irq)); | ||
| 132 | let spawner = executor.start(); | ||
| 133 | unwrap!(spawner.spawn(run_med())); | ||
| 134 | |||
| 135 | // Low priority executor: runs in thread mode, using WFE/SEV | ||
| 136 | let executor = EXECUTOR_LOW.init(Executor::new()); | ||
| 137 | executor.run(|spawner| { | ||
| 138 | unwrap!(spawner.spawn(run_low())); | ||
| 139 | }); | ||
| 140 | } | ||
diff --git a/examples/nrf52840/src/bin/mutex.rs b/examples/nrf52840/src/bin/mutex.rs new file mode 100644 index 000000000..c402c6ba1 --- /dev/null +++ b/examples/nrf52840/src/bin/mutex.rs | |||
| @@ -0,0 +1,42 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 8 | use embassy_sync::mutex::Mutex; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | static MUTEX: Mutex<ThreadModeRawMutex, u32> = Mutex::new(0); | ||
| 13 | |||
| 14 | #[embassy_executor::task] | ||
| 15 | async fn my_task() { | ||
| 16 | loop { | ||
| 17 | { | ||
| 18 | let mut m = MUTEX.lock().await; | ||
| 19 | info!("start long operation"); | ||
| 20 | *m += 1000; | ||
| 21 | |||
| 22 | // Hold the mutex for a long time. | ||
| 23 | Timer::after(Duration::from_secs(1)).await; | ||
| 24 | info!("end long operation: count = {}", *m); | ||
| 25 | } | ||
| 26 | |||
| 27 | Timer::after(Duration::from_secs(1)).await; | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | #[embassy_executor::main] | ||
| 32 | async fn main(spawner: Spawner) { | ||
| 33 | let _p = embassy_nrf::init(Default::default()); | ||
| 34 | unwrap!(spawner.spawn(my_task())); | ||
| 35 | |||
| 36 | loop { | ||
| 37 | Timer::after(Duration::from_millis(300)).await; | ||
| 38 | let mut m = MUTEX.lock().await; | ||
| 39 | *m += 1; | ||
| 40 | info!("short operation: count = {}", *m); | ||
| 41 | } | ||
| 42 | } | ||
diff --git a/examples/nrf52840/src/bin/nvmc.rs b/examples/nrf52840/src/bin/nvmc.rs new file mode 100644 index 000000000..75d090fbb --- /dev/null +++ b/examples/nrf52840/src/bin/nvmc.rs | |||
| @@ -0,0 +1,43 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::nvmc::Nvmc; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use embedded_storage::nor_flash::{NorFlash, ReadNorFlash}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let p = embassy_nrf::init(Default::default()); | ||
| 15 | info!("Hello NVMC!"); | ||
| 16 | |||
| 17 | // probe-run breaks without this, I'm not sure why. | ||
| 18 | Timer::after(Duration::from_secs(1)).await; | ||
| 19 | |||
| 20 | let mut f = Nvmc::new(p.NVMC); | ||
| 21 | const ADDR: u32 = 0x80000; | ||
| 22 | |||
| 23 | info!("Reading..."); | ||
| 24 | let mut buf = [0u8; 4]; | ||
| 25 | unwrap!(f.read(ADDR, &mut buf)); | ||
| 26 | info!("Read: {=[u8]:x}", buf); | ||
| 27 | |||
| 28 | info!("Erasing..."); | ||
| 29 | unwrap!(f.erase(ADDR, ADDR + 4096)); | ||
| 30 | |||
| 31 | info!("Reading..."); | ||
| 32 | let mut buf = [0u8; 4]; | ||
| 33 | unwrap!(f.read(ADDR, &mut buf)); | ||
| 34 | info!("Read: {=[u8]:x}", buf); | ||
| 35 | |||
| 36 | info!("Writing..."); | ||
| 37 | unwrap!(f.write(ADDR, &[1, 2, 3, 4])); | ||
| 38 | |||
| 39 | info!("Reading..."); | ||
| 40 | let mut buf = [0u8; 4]; | ||
| 41 | unwrap!(f.read(ADDR, &mut buf)); | ||
| 42 | info!("Read: {=[u8]:x}", buf); | ||
| 43 | } | ||
diff --git a/examples/nrf52840/src/bin/pdm.rs b/examples/nrf52840/src/bin/pdm.rs new file mode 100644 index 000000000..7388580fb --- /dev/null +++ b/examples/nrf52840/src/bin/pdm.rs | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::pdm::{Config, Pdm}; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_p: Spawner) { | ||
| 14 | let p = embassy_nrf::init(Default::default()); | ||
| 15 | let config = Config::default(); | ||
| 16 | let mut pdm = Pdm::new(p.PDM, interrupt::take!(PDM), p.P0_01, p.P0_00, config); | ||
| 17 | |||
| 18 | loop { | ||
| 19 | pdm.start().await; | ||
| 20 | |||
| 21 | // wait some time till the microphon settled | ||
| 22 | Timer::after(Duration::from_millis(1000)).await; | ||
| 23 | |||
| 24 | const SAMPLES: usize = 2048; | ||
| 25 | let mut buf = [0i16; SAMPLES]; | ||
| 26 | pdm.sample(&mut buf).await.unwrap(); | ||
| 27 | |||
| 28 | info!("samples: {:?}", &buf); | ||
| 29 | |||
| 30 | pdm.stop().await; | ||
| 31 | Timer::after(Duration::from_millis(100)).await; | ||
| 32 | } | ||
| 33 | } | ||
diff --git a/examples/nrf52840/src/bin/ppi.rs b/examples/nrf52840/src/bin/ppi.rs new file mode 100644 index 000000000..d74ce4064 --- /dev/null +++ b/examples/nrf52840/src/bin/ppi.rs | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::future::pending; | ||
| 6 | |||
| 7 | use defmt::info; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::gpio::{Input, Level, Output, OutputDrive, Pull}; | ||
| 10 | use embassy_nrf::gpiote::{self, InputChannel, InputChannelPolarity}; | ||
| 11 | use embassy_nrf::ppi::Ppi; | ||
| 12 | use gpiote::{OutputChannel, OutputChannelPolarity}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_nrf::init(Default::default()); | ||
| 18 | info!("Starting!"); | ||
| 19 | |||
| 20 | let button1 = InputChannel::new( | ||
| 21 | p.GPIOTE_CH0, | ||
| 22 | Input::new(p.P0_11, Pull::Up), | ||
| 23 | InputChannelPolarity::HiToLo, | ||
| 24 | ); | ||
| 25 | let button2 = InputChannel::new( | ||
| 26 | p.GPIOTE_CH1, | ||
| 27 | Input::new(p.P0_12, Pull::Up), | ||
| 28 | InputChannelPolarity::HiToLo, | ||
| 29 | ); | ||
| 30 | let button3 = InputChannel::new( | ||
| 31 | p.GPIOTE_CH2, | ||
| 32 | Input::new(p.P0_24, Pull::Up), | ||
| 33 | InputChannelPolarity::HiToLo, | ||
| 34 | ); | ||
| 35 | let button4 = InputChannel::new( | ||
| 36 | p.GPIOTE_CH3, | ||
| 37 | Input::new(p.P0_25, Pull::Up), | ||
| 38 | InputChannelPolarity::HiToLo, | ||
| 39 | ); | ||
| 40 | |||
| 41 | let led1 = OutputChannel::new( | ||
| 42 | p.GPIOTE_CH4, | ||
| 43 | Output::new(p.P0_13, Level::Low, OutputDrive::Standard), | ||
| 44 | OutputChannelPolarity::Toggle, | ||
| 45 | ); | ||
| 46 | |||
| 47 | let led2 = OutputChannel::new( | ||
| 48 | p.GPIOTE_CH5, | ||
| 49 | Output::new(p.P0_14, Level::Low, OutputDrive::Standard), | ||
| 50 | OutputChannelPolarity::Toggle, | ||
| 51 | ); | ||
| 52 | |||
| 53 | let mut ppi = Ppi::new_one_to_one(p.PPI_CH0, button1.event_in(), led1.task_out()); | ||
| 54 | ppi.enable(); | ||
| 55 | |||
| 56 | let mut ppi = Ppi::new_one_to_one(p.PPI_CH1, button2.event_in(), led1.task_clr()); | ||
| 57 | ppi.enable(); | ||
| 58 | |||
| 59 | let mut ppi = Ppi::new_one_to_one(p.PPI_CH2, button3.event_in(), led1.task_set()); | ||
| 60 | ppi.enable(); | ||
| 61 | |||
| 62 | let mut ppi = Ppi::new_one_to_two(p.PPI_CH3, button4.event_in(), led1.task_out(), led2.task_out()); | ||
| 63 | ppi.enable(); | ||
| 64 | |||
| 65 | info!("PPI setup!"); | ||
| 66 | info!("Press button 1 to toggle LED 1"); | ||
| 67 | info!("Press button 2 to turn on LED 1"); | ||
| 68 | info!("Press button 3 to turn off LED 1"); | ||
| 69 | info!("Press button 4 to toggle LEDs 1 and 2"); | ||
| 70 | |||
| 71 | // Block forever so the above drivers don't get dropped | ||
| 72 | pending::<()>().await; | ||
| 73 | } | ||
diff --git a/examples/nrf52840/src/bin/pubsub.rs b/examples/nrf52840/src/bin/pubsub.rs new file mode 100644 index 000000000..688e6d075 --- /dev/null +++ b/examples/nrf52840/src/bin/pubsub.rs | |||
| @@ -0,0 +1,107 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::unwrap; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 8 | use embassy_sync::pubsub::{DynSubscriber, PubSubChannel, Subscriber}; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | /// Create the message bus. It has a queue of 4, supports 3 subscribers and 1 publisher | ||
| 13 | static MESSAGE_BUS: PubSubChannel<ThreadModeRawMutex, Message, 4, 3, 1> = PubSubChannel::new(); | ||
| 14 | |||
| 15 | #[derive(Clone, defmt::Format)] | ||
| 16 | enum Message { | ||
| 17 | A, | ||
| 18 | B, | ||
| 19 | C, | ||
| 20 | } | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(spawner: Spawner) { | ||
| 24 | let _p = embassy_nrf::init(Default::default()); | ||
| 25 | defmt::info!("Hello World!"); | ||
| 26 | |||
| 27 | // It's good to set up the subscribers before publishing anything. | ||
| 28 | // A subscriber will only yield messages that have been published after its creation. | ||
| 29 | |||
| 30 | spawner.must_spawn(fast_logger(unwrap!(MESSAGE_BUS.subscriber()))); | ||
| 31 | spawner.must_spawn(slow_logger(unwrap!(MESSAGE_BUS.dyn_subscriber()))); | ||
| 32 | spawner.must_spawn(slow_logger_pure(unwrap!(MESSAGE_BUS.dyn_subscriber()))); | ||
| 33 | |||
| 34 | // Get a publisher | ||
| 35 | let message_publisher = unwrap!(MESSAGE_BUS.publisher()); | ||
| 36 | // We can't get more (normal) publishers | ||
| 37 | // We can have an infinite amount of immediate publishers. They can't await a publish, only do an immediate publish | ||
| 38 | defmt::assert!(MESSAGE_BUS.publisher().is_err()); | ||
| 39 | |||
| 40 | let mut index = 0; | ||
| 41 | loop { | ||
| 42 | Timer::after(Duration::from_millis(500)).await; | ||
| 43 | |||
| 44 | let message = match index % 3 { | ||
| 45 | 0 => Message::A, | ||
| 46 | 1 => Message::B, | ||
| 47 | 2..=u32::MAX => Message::C, | ||
| 48 | }; | ||
| 49 | |||
| 50 | // We publish immediately and don't await anything. | ||
| 51 | // If the queue is full, it will cause the oldest message to not be received by some/all subscribers | ||
| 52 | message_publisher.publish_immediate(message); | ||
| 53 | |||
| 54 | // Try to comment out the last one and uncomment this line below. | ||
| 55 | // The behaviour will change: | ||
| 56 | // - The subscribers won't miss any messages any more | ||
| 57 | // - Trying to publish now has some wait time when the queue is full | ||
| 58 | |||
| 59 | // message_publisher.publish(message).await; | ||
| 60 | |||
| 61 | index += 1; | ||
| 62 | } | ||
| 63 | } | ||
| 64 | |||
| 65 | /// A logger task that just awaits the messages it receives | ||
| 66 | /// | ||
| 67 | /// This takes the generic `Subscriber`. This is most performant, but requires you to write down all of the generics | ||
| 68 | #[embassy_executor::task] | ||
| 69 | async fn fast_logger(mut messages: Subscriber<'static, ThreadModeRawMutex, Message, 4, 3, 1>) { | ||
| 70 | loop { | ||
| 71 | let message = messages.next_message().await; | ||
| 72 | defmt::info!("Received message at fast logger: {:?}", message); | ||
| 73 | } | ||
| 74 | } | ||
| 75 | |||
| 76 | /// A logger task that awaits the messages, but also does some other work. | ||
| 77 | /// Because of this, depeding on how the messages were published, the subscriber might miss some messages | ||
| 78 | /// | ||
| 79 | /// This takes the dynamic `DynSubscriber`. This is not as performant as the generic version, but let's you ignore some of the generics | ||
| 80 | #[embassy_executor::task] | ||
| 81 | async fn slow_logger(mut messages: DynSubscriber<'static, Message>) { | ||
| 82 | loop { | ||
| 83 | // Do some work | ||
| 84 | Timer::after(Duration::from_millis(2000)).await; | ||
| 85 | |||
| 86 | // If the publisher has used the `publish_immediate` function, then we may receive a lag message here | ||
| 87 | let message = messages.next_message().await; | ||
| 88 | defmt::info!("Received message at slow logger: {:?}", message); | ||
| 89 | |||
| 90 | // If the previous one was a lag message, then we should receive the next message here immediately | ||
| 91 | let message = messages.next_message().await; | ||
| 92 | defmt::info!("Received message at slow logger: {:?}", message); | ||
| 93 | } | ||
| 94 | } | ||
| 95 | |||
| 96 | /// Same as `slow_logger` but it ignores lag results | ||
| 97 | #[embassy_executor::task] | ||
| 98 | async fn slow_logger_pure(mut messages: DynSubscriber<'static, Message>) { | ||
| 99 | loop { | ||
| 100 | // Do some work | ||
| 101 | Timer::after(Duration::from_millis(2000)).await; | ||
| 102 | |||
| 103 | // Instead of receiving lags here, we just ignore that and read the next message | ||
| 104 | let message = messages.next_message_pure().await; | ||
| 105 | defmt::info!("Received message at slow logger pure: {:?}", message); | ||
| 106 | } | ||
| 107 | } | ||
diff --git a/examples/nrf52840/src/bin/pwm.rs b/examples/nrf52840/src/bin/pwm.rs new file mode 100644 index 000000000..1698c0bc8 --- /dev/null +++ b/examples/nrf52840/src/bin/pwm.rs | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::pwm::{Prescaler, SimplePwm}; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | // for i in range(1024): print(int((math.sin(i/512*math.pi)*0.4+0.5)**2*32767), ', ', end='') | ||
| 12 | static DUTY: [u16; 1024] = [ | ||
| 13 | 8191, 8272, 8353, 8434, 8516, 8598, 8681, 8764, 8847, 8931, 9015, 9099, 9184, 9269, 9354, 9440, 9526, 9613, 9700, | ||
| 14 | 9787, 9874, 9962, 10050, 10139, 10227, 10316, 10406, 10495, 10585, 10675, 10766, 10857, 10948, 11039, 11131, 11223, | ||
| 15 | 11315, 11407, 11500, 11592, 11685, 11779, 11872, 11966, 12060, 12154, 12248, 12343, 12438, 12533, 12628, 12723, | ||
| 16 | 12818, 12914, 13010, 13106, 13202, 13298, 13394, 13491, 13587, 13684, 13781, 13878, 13975, 14072, 14169, 14266, | ||
| 17 | 14364, 14461, 14558, 14656, 14754, 14851, 14949, 15046, 15144, 15242, 15339, 15437, 15535, 15632, 15730, 15828, | ||
| 18 | 15925, 16023, 16120, 16218, 16315, 16412, 16510, 16607, 16704, 16801, 16898, 16995, 17091, 17188, 17284, 17380, | ||
| 19 | 17477, 17572, 17668, 17764, 17859, 17955, 18050, 18145, 18239, 18334, 18428, 18522, 18616, 18710, 18803, 18896, | ||
| 20 | 18989, 19082, 19174, 19266, 19358, 19449, 19540, 19631, 19722, 19812, 19902, 19991, 20081, 20169, 20258, 20346, | ||
| 21 | 20434, 20521, 20608, 20695, 20781, 20867, 20952, 21037, 21122, 21206, 21290, 21373, 21456, 21538, 21620, 21701, | ||
| 22 | 21782, 21863, 21943, 22022, 22101, 22179, 22257, 22335, 22412, 22488, 22564, 22639, 22714, 22788, 22861, 22934, | ||
| 23 | 23007, 23079, 23150, 23220, 23290, 23360, 23429, 23497, 23564, 23631, 23698, 23763, 23828, 23892, 23956, 24019, | ||
| 24 | 24081, 24143, 24204, 24264, 24324, 24383, 24441, 24499, 24555, 24611, 24667, 24721, 24775, 24828, 24881, 24933, | ||
| 25 | 24983, 25034, 25083, 25132, 25180, 25227, 25273, 25319, 25363, 25407, 25451, 25493, 25535, 25575, 25615, 25655, | ||
| 26 | 25693, 25731, 25767, 25803, 25838, 25873, 25906, 25939, 25971, 26002, 26032, 26061, 26089, 26117, 26144, 26170, | ||
| 27 | 26195, 26219, 26242, 26264, 26286, 26307, 26327, 26346, 26364, 26381, 26397, 26413, 26427, 26441, 26454, 26466, | ||
| 28 | 26477, 26487, 26496, 26505, 26512, 26519, 26525, 26530, 26534, 26537, 26539, 26540, 26541, 26540, 26539, 26537, | ||
| 29 | 26534, 26530, 26525, 26519, 26512, 26505, 26496, 26487, 26477, 26466, 26454, 26441, 26427, 26413, 26397, 26381, | ||
| 30 | 26364, 26346, 26327, 26307, 26286, 26264, 26242, 26219, 26195, 26170, 26144, 26117, 26089, 26061, 26032, 26002, | ||
| 31 | 25971, 25939, 25906, 25873, 25838, 25803, 25767, 25731, 25693, 25655, 25615, 25575, 25535, 25493, 25451, 25407, | ||
| 32 | 25363, 25319, 25273, 25227, 25180, 25132, 25083, 25034, 24983, 24933, 24881, 24828, 24775, 24721, 24667, 24611, | ||
| 33 | 24555, 24499, 24441, 24383, 24324, 24264, 24204, 24143, 24081, 24019, 23956, 23892, 23828, 23763, 23698, 23631, | ||
| 34 | 23564, 23497, 23429, 23360, 23290, 23220, 23150, 23079, 23007, 22934, 22861, 22788, 22714, 22639, 22564, 22488, | ||
| 35 | 22412, 22335, 22257, 22179, 22101, 22022, 21943, 21863, 21782, 21701, 21620, 21538, 21456, 21373, 21290, 21206, | ||
| 36 | 21122, 21037, 20952, 20867, 20781, 20695, 20608, 20521, 20434, 20346, 20258, 20169, 20081, 19991, 19902, 19812, | ||
| 37 | 19722, 19631, 19540, 19449, 19358, 19266, 19174, 19082, 18989, 18896, 18803, 18710, 18616, 18522, 18428, 18334, | ||
| 38 | 18239, 18145, 18050, 17955, 17859, 17764, 17668, 17572, 17477, 17380, 17284, 17188, 17091, 16995, 16898, 16801, | ||
| 39 | 16704, 16607, 16510, 16412, 16315, 16218, 16120, 16023, 15925, 15828, 15730, 15632, 15535, 15437, 15339, 15242, | ||
| 40 | 15144, 15046, 14949, 14851, 14754, 14656, 14558, 14461, 14364, 14266, 14169, 14072, 13975, 13878, 13781, 13684, | ||
| 41 | 13587, 13491, 13394, 13298, 13202, 13106, 13010, 12914, 12818, 12723, 12628, 12533, 12438, 12343, 12248, 12154, | ||
| 42 | 12060, 11966, 11872, 11779, 11685, 11592, 11500, 11407, 11315, 11223, 11131, 11039, 10948, 10857, 10766, 10675, | ||
| 43 | 10585, 10495, 10406, 10316, 10227, 10139, 10050, 9962, 9874, 9787, 9700, 9613, 9526, 9440, 9354, 9269, 9184, 9099, | ||
| 44 | 9015, 8931, 8847, 8764, 8681, 8598, 8516, 8434, 8353, 8272, 8191, 8111, 8031, 7952, 7873, 7794, 7716, 7638, 7561, | ||
| 45 | 7484, 7407, 7331, 7255, 7180, 7105, 7031, 6957, 6883, 6810, 6738, 6665, 6594, 6522, 6451, 6381, 6311, 6241, 6172, | ||
| 46 | 6104, 6036, 5968, 5901, 5834, 5767, 5702, 5636, 5571, 5507, 5443, 5379, 5316, 5253, 5191, 5130, 5068, 5008, 4947, | ||
| 47 | 4888, 4828, 4769, 4711, 4653, 4596, 4539, 4482, 4426, 4371, 4316, 4261, 4207, 4153, 4100, 4047, 3995, 3943, 3892, | ||
| 48 | 3841, 3791, 3741, 3691, 3642, 3594, 3546, 3498, 3451, 3404, 3358, 3312, 3267, 3222, 3178, 3134, 3090, 3047, 3005, | ||
| 49 | 2962, 2921, 2879, 2839, 2798, 2758, 2719, 2680, 2641, 2603, 2565, 2528, 2491, 2454, 2418, 2382, 2347, 2312, 2278, | ||
| 50 | 2244, 2210, 2177, 2144, 2112, 2080, 2048, 2017, 1986, 1956, 1926, 1896, 1867, 1838, 1810, 1781, 1754, 1726, 1699, | ||
| 51 | 1673, 1646, 1620, 1595, 1570, 1545, 1520, 1496, 1472, 1449, 1426, 1403, 1380, 1358, 1336, 1315, 1294, 1273, 1252, | ||
| 52 | 1232, 1212, 1192, 1173, 1154, 1135, 1117, 1099, 1081, 1063, 1046, 1029, 1012, 996, 980, 964, 948, 933, 918, 903, | ||
| 53 | 888, 874, 860, 846, 833, 819, 806, 793, 781, 768, 756, 744, 733, 721, 710, 699, 688, 677, 667, 657, 647, 637, 627, | ||
| 54 | 618, 609, 599, 591, 582, 574, 565, 557, 549, 541, 534, 526, 519, 512, 505, 498, 492, 485, 479, 473, 467, 461, 455, | ||
| 55 | 450, 444, 439, 434, 429, 424, 419, 415, 410, 406, 402, 398, 394, 390, 386, 383, 379, 376, 373, 370, 367, 364, 361, | ||
| 56 | 359, 356, 354, 351, 349, 347, 345, 343, 342, 340, 338, 337, 336, 334, 333, 332, 331, 330, 330, 329, 328, 328, 328, | ||
| 57 | 327, 327, 327, 327, 327, 328, 328, 328, 329, 330, 330, 331, 332, 333, 334, 336, 337, 338, 340, 342, 343, 345, 347, | ||
| 58 | 349, 351, 354, 356, 359, 361, 364, 367, 370, 373, 376, 379, 383, 386, 390, 394, 398, 402, 406, 410, 415, 419, 424, | ||
| 59 | 429, 434, 439, 444, 450, 455, 461, 467, 473, 479, 485, 492, 498, 505, 512, 519, 526, 534, 541, 549, 557, 565, 574, | ||
| 60 | 582, 591, 599, 609, 618, 627, 637, 647, 657, 667, 677, 688, 699, 710, 721, 733, 744, 756, 768, 781, 793, 806, 819, | ||
| 61 | 833, 846, 860, 874, 888, 903, 918, 933, 948, 964, 980, 996, 1012, 1029, 1046, 1063, 1081, 1099, 1117, 1135, 1154, | ||
| 62 | 1173, 1192, 1212, 1232, 1252, 1273, 1294, 1315, 1336, 1358, 1380, 1403, 1426, 1449, 1472, 1496, 1520, 1545, 1570, | ||
| 63 | 1595, 1620, 1646, 1673, 1699, 1726, 1754, 1781, 1810, 1838, 1867, 1896, 1926, 1956, 1986, 2017, 2048, 2080, 2112, | ||
| 64 | 2144, 2177, 2210, 2244, 2278, 2312, 2347, 2382, 2418, 2454, 2491, 2528, 2565, 2603, 2641, 2680, 2719, 2758, 2798, | ||
| 65 | 2839, 2879, 2921, 2962, 3005, 3047, 3090, 3134, 3178, 3222, 3267, 3312, 3358, 3404, 3451, 3498, 3546, 3594, 3642, | ||
| 66 | 3691, 3741, 3791, 3841, 3892, 3943, 3995, 4047, 4100, 4153, 4207, 4261, 4316, 4371, 4426, 4482, 4539, 4596, 4653, | ||
| 67 | 4711, 4769, 4828, 4888, 4947, 5008, 5068, 5130, 5191, 5253, 5316, 5379, 5443, 5507, 5571, 5636, 5702, 5767, 5834, | ||
| 68 | 5901, 5968, 6036, 6104, 6172, 6241, 6311, 6381, 6451, 6522, 6594, 6665, 6738, 6810, 6883, 6957, 7031, 7105, 7180, | ||
| 69 | 7255, 7331, 7407, 7484, 7561, 7638, 7716, 7794, 7873, 7952, 8031, 8111, | ||
| 70 | ]; | ||
| 71 | |||
| 72 | #[embassy_executor::main] | ||
| 73 | async fn main(_spawner: Spawner) { | ||
| 74 | let p = embassy_nrf::init(Default::default()); | ||
| 75 | let mut pwm = SimplePwm::new_4ch(p.PWM0, p.P0_13, p.P0_14, p.P0_16, p.P0_15); | ||
| 76 | pwm.set_prescaler(Prescaler::Div1); | ||
| 77 | pwm.set_max_duty(32767); | ||
| 78 | info!("pwm initialized!"); | ||
| 79 | |||
| 80 | let mut i = 0; | ||
| 81 | loop { | ||
| 82 | i += 1; | ||
| 83 | pwm.set_duty(0, DUTY[i % 1024]); | ||
| 84 | pwm.set_duty(1, DUTY[(i + 256) % 1024]); | ||
| 85 | pwm.set_duty(2, DUTY[(i + 512) % 1024]); | ||
| 86 | pwm.set_duty(3, DUTY[(i + 768) % 1024]); | ||
| 87 | Timer::after(Duration::from_millis(3)).await; | ||
| 88 | } | ||
| 89 | } | ||
diff --git a/examples/nrf52840/src/bin/pwm_double_sequence.rs b/examples/nrf52840/src/bin/pwm_double_sequence.rs new file mode 100644 index 000000000..16e50e909 --- /dev/null +++ b/examples/nrf52840/src/bin/pwm_double_sequence.rs | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::pwm::{ | ||
| 8 | Config, Prescaler, Sequence, SequenceConfig, SequenceMode, SequencePwm, Sequencer, StartSequence, | ||
| 9 | }; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) { | ||
| 15 | let p = embassy_nrf::init(Default::default()); | ||
| 16 | let seq_words_0: [u16; 5] = [1000, 250, 100, 50, 0]; | ||
| 17 | let seq_words_1: [u16; 4] = [50, 100, 250, 1000]; | ||
| 18 | |||
| 19 | let mut config = Config::default(); | ||
| 20 | config.prescaler = Prescaler::Div128; | ||
| 21 | // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us | ||
| 22 | // but say we want to hold the value for 5000ms | ||
| 23 | // so we want to repeat our value as many times as necessary until 5000ms passes | ||
| 24 | // want 5000/8 = 625 periods total to occur, so 624 (we get the one period for free remember) | ||
| 25 | let mut seq_config = SequenceConfig::default(); | ||
| 26 | seq_config.refresh = 624; | ||
| 27 | // thus our sequence takes 5 * 5000ms or 25 seconds | ||
| 28 | |||
| 29 | let mut pwm = unwrap!(SequencePwm::new_1ch(p.PWM0, p.P0_13, config)); | ||
| 30 | |||
| 31 | let sequence_0 = Sequence::new(&seq_words_0, seq_config.clone()); | ||
| 32 | let sequence_1 = Sequence::new(&seq_words_1, seq_config); | ||
| 33 | let sequencer = Sequencer::new(&mut pwm, sequence_0, Some(sequence_1)); | ||
| 34 | unwrap!(sequencer.start(StartSequence::Zero, SequenceMode::Loop(1))); | ||
| 35 | |||
| 36 | // we can abort a sequence if we need to before its complete with pwm.stop() | ||
| 37 | // or stop is also implicitly called when the pwm peripheral is dropped | ||
| 38 | // when it goes out of scope | ||
| 39 | Timer::after(Duration::from_millis(40000)).await; | ||
| 40 | info!("pwm stopped early!"); | ||
| 41 | } | ||
diff --git a/examples/nrf52840/src/bin/pwm_sequence.rs b/examples/nrf52840/src/bin/pwm_sequence.rs new file mode 100644 index 000000000..b9aca9aaa --- /dev/null +++ b/examples/nrf52840/src/bin/pwm_sequence.rs | |||
| @@ -0,0 +1,36 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::pwm::{Config, Prescaler, SequenceConfig, SequencePwm, SingleSequenceMode, SingleSequencer}; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_nrf::init(Default::default()); | ||
| 14 | let seq_words: [u16; 5] = [1000, 250, 100, 50, 0]; | ||
| 15 | |||
| 16 | let mut config = Config::default(); | ||
| 17 | config.prescaler = Prescaler::Div128; | ||
| 18 | // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us | ||
| 19 | // but say we want to hold the value for 5000ms | ||
| 20 | // so we want to repeat our value as many times as necessary until 5000ms passes | ||
| 21 | // want 5000/8 = 625 periods total to occur, so 624 (we get the one period for free remember) | ||
| 22 | let mut seq_config = SequenceConfig::default(); | ||
| 23 | seq_config.refresh = 624; | ||
| 24 | // thus our sequence takes 5 * 5000ms or 25 seconds | ||
| 25 | |||
| 26 | let mut pwm = unwrap!(SequencePwm::new_1ch(p.PWM0, p.P0_13, config,)); | ||
| 27 | |||
| 28 | let sequencer = SingleSequencer::new(&mut pwm, &seq_words, seq_config); | ||
| 29 | unwrap!(sequencer.start(SingleSequenceMode::Times(1))); | ||
| 30 | |||
| 31 | // we can abort a sequence if we need to before its complete with pwm.stop() | ||
| 32 | // or stop is also implicitly called when the pwm peripheral is dropped | ||
| 33 | // when it goes out of scope | ||
| 34 | Timer::after(Duration::from_millis(20000)).await; | ||
| 35 | info!("pwm stopped early!"); | ||
| 36 | } | ||
diff --git a/examples/nrf52840/src/bin/pwm_sequence_ppi.rs b/examples/nrf52840/src/bin/pwm_sequence_ppi.rs new file mode 100644 index 000000000..6594fa348 --- /dev/null +++ b/examples/nrf52840/src/bin/pwm_sequence_ppi.rs | |||
| @@ -0,0 +1,67 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::future::pending; | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::gpio::{Input, Pull}; | ||
| 10 | use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; | ||
| 11 | use embassy_nrf::ppi::Ppi; | ||
| 12 | use embassy_nrf::pwm::{Config, Prescaler, SequenceConfig, SequencePwm, SingleSequenceMode, SingleSequencer}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_spawner: Spawner) { | ||
| 17 | let p = embassy_nrf::init(Default::default()); | ||
| 18 | let seq_words: [u16; 5] = [1000, 250, 100, 50, 0]; | ||
| 19 | |||
| 20 | let mut config = Config::default(); | ||
| 21 | config.prescaler = Prescaler::Div128; | ||
| 22 | // 1 period is 1000 * (128/16mhz = 0.000008s = 0.008ms) = 8us | ||
| 23 | // but say we want to hold the value for 250ms 250ms/8 = 31.25 periods | ||
| 24 | // so round to 31 - 1 (we get the one period for free remember) | ||
| 25 | // thus our sequence takes 5 * 250ms or 1.25 seconds | ||
| 26 | let mut seq_config = SequenceConfig::default(); | ||
| 27 | seq_config.refresh = 30; | ||
| 28 | |||
| 29 | let mut pwm = unwrap!(SequencePwm::new_1ch(p.PWM0, p.P0_13, config)); | ||
| 30 | |||
| 31 | // pwm.stop() deconfigures pins, and then the task_start_seq0 task cant work | ||
| 32 | // so its going to have to start running in order load the configuration | ||
| 33 | |||
| 34 | let button1 = InputChannel::new( | ||
| 35 | p.GPIOTE_CH0, | ||
| 36 | Input::new(p.P0_11, Pull::Up), | ||
| 37 | InputChannelPolarity::HiToLo, | ||
| 38 | ); | ||
| 39 | |||
| 40 | let button2 = InputChannel::new( | ||
| 41 | p.GPIOTE_CH1, | ||
| 42 | Input::new(p.P0_12, Pull::Up), | ||
| 43 | InputChannelPolarity::HiToLo, | ||
| 44 | ); | ||
| 45 | |||
| 46 | // messing with the pwm tasks is ill advised | ||
| 47 | // Times::Ininite and Times even are seq0, Times odd is seq1 | ||
| 48 | let start = unsafe { pwm.task_start_seq0() }; | ||
| 49 | let stop = unsafe { pwm.task_stop() }; | ||
| 50 | |||
| 51 | let sequencer = SingleSequencer::new(&mut pwm, &seq_words, seq_config); | ||
| 52 | unwrap!(sequencer.start(SingleSequenceMode::Infinite)); | ||
| 53 | |||
| 54 | let mut ppi = Ppi::new_one_to_one(p.PPI_CH1, button1.event_in(), start); | ||
| 55 | ppi.enable(); | ||
| 56 | |||
| 57 | let mut ppi2 = Ppi::new_one_to_one(p.PPI_CH0, button2.event_in(), stop); | ||
| 58 | ppi2.enable(); | ||
| 59 | |||
| 60 | info!("PPI setup!"); | ||
| 61 | info!("Press button 1 to start LED 1"); | ||
| 62 | info!("Press button 2 to stop LED 1"); | ||
| 63 | info!("Note! task_stop stops the sequence, but not the pin output"); | ||
| 64 | |||
| 65 | // Block forever so the above drivers don't get dropped | ||
| 66 | pending::<()>().await; | ||
| 67 | } | ||
diff --git a/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs b/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs new file mode 100644 index 000000000..711c8a17b --- /dev/null +++ b/examples/nrf52840/src/bin/pwm_sequence_ws2812b.rs | |||
| @@ -0,0 +1,75 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::pwm::{ | ||
| 8 | Config, Prescaler, SequenceConfig, SequenceLoad, SequencePwm, SingleSequenceMode, SingleSequencer, | ||
| 9 | }; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | // WS2812B LED light demonstration. Drives just one light. | ||
| 14 | // The following reference on WS2812B may be of use: | ||
| 15 | // https://cdn-shop.adafruit.com/datasheets/WS2812B.pdf. | ||
| 16 | // This demo lights up a single LED in blue. It then proceeds | ||
| 17 | // to pulsate the LED rapidly. | ||
| 18 | |||
| 19 | // In the following declarations, setting the high bit tells the PWM | ||
| 20 | // to reverse polarity, which is what the WS2812B expects. | ||
| 21 | |||
| 22 | const T1H: u16 = 0x8000 | 13; // Duty = 13/20 ticks (0.8us/1.25us) for a 1 | ||
| 23 | const T0H: u16 = 0x8000 | 7; // Duty 7/20 ticks (0.4us/1.25us) for a 0 | ||
| 24 | const RES: u16 = 0x8000; | ||
| 25 | |||
| 26 | // Provides data to a WS2812b (Neopixel) LED and makes it go blue. The data | ||
| 27 | // line is assumed to be P1_05. | ||
| 28 | #[embassy_executor::main] | ||
| 29 | async fn main(_spawner: Spawner) { | ||
| 30 | let p = embassy_nrf::init(Default::default()); | ||
| 31 | let mut config = Config::default(); | ||
| 32 | config.sequence_load = SequenceLoad::Common; | ||
| 33 | config.prescaler = Prescaler::Div1; | ||
| 34 | config.max_duty = 20; // 1.25us (1s / 16Mhz * 20) | ||
| 35 | let mut pwm = unwrap!(SequencePwm::new_1ch(p.PWM0, p.P1_05, config)); | ||
| 36 | |||
| 37 | // Declare the bits of 24 bits in a buffer we'll be | ||
| 38 | // mutating later. | ||
| 39 | let mut seq_words = [ | ||
| 40 | T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // G | ||
| 41 | T0H, T0H, T0H, T0H, T0H, T0H, T0H, T0H, // R | ||
| 42 | T1H, T1H, T1H, T1H, T1H, T1H, T1H, T1H, // B | ||
| 43 | RES, | ||
| 44 | ]; | ||
| 45 | let mut seq_config = SequenceConfig::default(); | ||
| 46 | seq_config.end_delay = 799; // 50us (20 ticks * 40) - 1 tick because we've already got one RES; | ||
| 47 | |||
| 48 | let mut color_bit = 16; | ||
| 49 | let mut bit_value = T0H; | ||
| 50 | |||
| 51 | loop { | ||
| 52 | let sequences = SingleSequencer::new(&mut pwm, &seq_words, seq_config.clone()); | ||
| 53 | unwrap!(sequences.start(SingleSequenceMode::Times(1))); | ||
| 54 | |||
| 55 | Timer::after(Duration::from_millis(50)).await; | ||
| 56 | |||
| 57 | if bit_value == T0H { | ||
| 58 | if color_bit == 20 { | ||
| 59 | bit_value = T1H; | ||
| 60 | } else { | ||
| 61 | color_bit += 1; | ||
| 62 | } | ||
| 63 | } else { | ||
| 64 | if color_bit == 16 { | ||
| 65 | bit_value = T0H; | ||
| 66 | } else { | ||
| 67 | color_bit -= 1; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | drop(sequences); | ||
| 72 | |||
| 73 | seq_words[color_bit] = bit_value; | ||
| 74 | } | ||
| 75 | } | ||
diff --git a/examples/nrf52840/src/bin/pwm_servo.rs b/examples/nrf52840/src/bin/pwm_servo.rs new file mode 100644 index 000000000..19228f433 --- /dev/null +++ b/examples/nrf52840/src/bin/pwm_servo.rs | |||
| @@ -0,0 +1,47 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::pwm::{Prescaler, SimplePwm}; | ||
| 8 | use embassy_time::{Duration, Timer}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_nrf::init(Default::default()); | ||
| 14 | let mut pwm = SimplePwm::new_1ch(p.PWM0, p.P0_05); | ||
| 15 | // sg90 microervo requires 50hz or 20ms period | ||
| 16 | // set_period can only set down to 125khz so we cant use it directly | ||
| 17 | // Div128 is 125khz or 0.000008s or 0.008ms, 20/0.008 is 2500 is top | ||
| 18 | pwm.set_prescaler(Prescaler::Div128); | ||
| 19 | pwm.set_max_duty(2500); | ||
| 20 | info!("pwm initialized!"); | ||
| 21 | |||
| 22 | Timer::after(Duration::from_millis(5000)).await; | ||
| 23 | |||
| 24 | // 1ms 0deg (1/.008=125), 1.5ms 90deg (1.5/.008=187.5), 2ms 180deg (2/.008=250), | ||
| 25 | loop { | ||
| 26 | info!("45 deg"); | ||
| 27 | // poor mans inverting, subtract our value from max_duty | ||
| 28 | pwm.set_duty(0, 2500 - 156); | ||
| 29 | Timer::after(Duration::from_millis(5000)).await; | ||
| 30 | |||
| 31 | info!("90 deg"); | ||
| 32 | pwm.set_duty(0, 2500 - 187); | ||
| 33 | Timer::after(Duration::from_millis(5000)).await; | ||
| 34 | |||
| 35 | info!("135 deg"); | ||
| 36 | pwm.set_duty(0, 2500 - 218); | ||
| 37 | Timer::after(Duration::from_millis(5000)).await; | ||
| 38 | |||
| 39 | info!("180 deg"); | ||
| 40 | pwm.set_duty(0, 2500 - 250); | ||
| 41 | Timer::after(Duration::from_millis(5000)).await; | ||
| 42 | |||
| 43 | info!("0 deg"); | ||
| 44 | pwm.set_duty(0, 2500 - 125); | ||
| 45 | Timer::after(Duration::from_millis(5000)).await; | ||
| 46 | } | ||
| 47 | } | ||
diff --git a/examples/nrf52840/src/bin/qdec.rs b/examples/nrf52840/src/bin/qdec.rs new file mode 100644 index 000000000..600bba07a --- /dev/null +++ b/examples/nrf52840/src/bin/qdec.rs | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::qdec::{self, Qdec}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_nrf::init(Default::default()); | ||
| 14 | let irq = interrupt::take!(QDEC); | ||
| 15 | let config = qdec::Config::default(); | ||
| 16 | let mut rotary_enc = Qdec::new(p.QDEC, irq, p.P0_31, p.P0_30, config); | ||
| 17 | |||
| 18 | info!("Turn rotary encoder!"); | ||
| 19 | let mut value = 0; | ||
| 20 | loop { | ||
| 21 | value += rotary_enc.read().await; | ||
| 22 | info!("Value: {}", value); | ||
| 23 | } | ||
| 24 | } | ||
diff --git a/examples/nrf52840/src/bin/qspi.rs b/examples/nrf52840/src/bin/qspi.rs new file mode 100644 index 000000000..bdcf710b8 --- /dev/null +++ b/examples/nrf52840/src/bin/qspi.rs | |||
| @@ -0,0 +1,76 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{assert_eq, info, unwrap}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::{interrupt, qspi}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | const PAGE_SIZE: usize = 4096; | ||
| 11 | |||
| 12 | // Workaround for alignment requirements. | ||
| 13 | // Nicer API will probably come in the future. | ||
| 14 | #[repr(C, align(4))] | ||
| 15 | struct AlignedBuf([u8; 4096]); | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_nrf::init(Default::default()); | ||
| 20 | // Config for the MX25R64 present in the nRF52840 DK | ||
| 21 | let mut config = qspi::Config::default(); | ||
| 22 | config.read_opcode = qspi::ReadOpcode::READ4IO; | ||
| 23 | config.write_opcode = qspi::WriteOpcode::PP4IO; | ||
| 24 | config.write_page_size = qspi::WritePageSize::_256BYTES; | ||
| 25 | |||
| 26 | let irq = interrupt::take!(QSPI); | ||
| 27 | let mut q: qspi::Qspi<_, 67108864> = qspi::Qspi::new( | ||
| 28 | p.QSPI, irq, p.P0_19, p.P0_17, p.P0_20, p.P0_21, p.P0_22, p.P0_23, config, | ||
| 29 | ); | ||
| 30 | |||
| 31 | let mut id = [1; 3]; | ||
| 32 | unwrap!(q.custom_instruction(0x9F, &[], &mut id).await); | ||
| 33 | info!("id: {}", id); | ||
| 34 | |||
| 35 | // Read status register | ||
| 36 | let mut status = [4; 1]; | ||
| 37 | unwrap!(q.custom_instruction(0x05, &[], &mut status).await); | ||
| 38 | |||
| 39 | info!("status: {:?}", status[0]); | ||
| 40 | |||
| 41 | if status[0] & 0x40 == 0 { | ||
| 42 | status[0] |= 0x40; | ||
| 43 | |||
| 44 | unwrap!(q.custom_instruction(0x01, &status, &mut []).await); | ||
| 45 | |||
| 46 | info!("enabled quad in status"); | ||
| 47 | } | ||
| 48 | |||
| 49 | let mut buf = AlignedBuf([0u8; PAGE_SIZE]); | ||
| 50 | |||
| 51 | let pattern = |a: u32| (a ^ (a >> 8) ^ (a >> 16) ^ (a >> 24)) as u8; | ||
| 52 | |||
| 53 | for i in 0..8 { | ||
| 54 | info!("page {:?}: erasing... ", i); | ||
| 55 | unwrap!(q.erase(i * PAGE_SIZE).await); | ||
| 56 | |||
| 57 | for j in 0..PAGE_SIZE { | ||
| 58 | buf.0[j] = pattern((j + i * PAGE_SIZE) as u32); | ||
| 59 | } | ||
| 60 | |||
| 61 | info!("programming..."); | ||
| 62 | unwrap!(q.write(i * PAGE_SIZE, &buf.0).await); | ||
| 63 | } | ||
| 64 | |||
| 65 | for i in 0..8 { | ||
| 66 | info!("page {:?}: reading... ", i); | ||
| 67 | unwrap!(q.read(i * PAGE_SIZE, &mut buf.0).await); | ||
| 68 | |||
| 69 | info!("verifying..."); | ||
| 70 | for j in 0..PAGE_SIZE { | ||
| 71 | assert_eq!(buf.0[j], pattern((j + i * PAGE_SIZE) as u32)); | ||
| 72 | } | ||
| 73 | } | ||
| 74 | |||
| 75 | info!("done!") | ||
| 76 | } | ||
diff --git a/examples/nrf52840/src/bin/qspi_lowpower.rs b/examples/nrf52840/src/bin/qspi_lowpower.rs new file mode 100644 index 000000000..9341a2376 --- /dev/null +++ b/examples/nrf52840/src/bin/qspi_lowpower.rs | |||
| @@ -0,0 +1,78 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::mem; | ||
| 6 | |||
| 7 | use defmt::{info, unwrap}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::{interrupt, qspi}; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | // Workaround for alignment requirements. | ||
| 14 | // Nicer API will probably come in the future. | ||
| 15 | #[repr(C, align(4))] | ||
| 16 | struct AlignedBuf([u8; 64]); | ||
| 17 | |||
| 18 | #[embassy_executor::main] | ||
| 19 | async fn main(_p: Spawner) { | ||
| 20 | let mut p = embassy_nrf::init(Default::default()); | ||
| 21 | let mut irq = interrupt::take!(QSPI); | ||
| 22 | |||
| 23 | loop { | ||
| 24 | // Config for the MX25R64 present in the nRF52840 DK | ||
| 25 | let mut config = qspi::Config::default(); | ||
| 26 | config.read_opcode = qspi::ReadOpcode::READ4IO; | ||
| 27 | config.write_opcode = qspi::WriteOpcode::PP4IO; | ||
| 28 | config.write_page_size = qspi::WritePageSize::_256BYTES; | ||
| 29 | config.deep_power_down = Some(qspi::DeepPowerDownConfig { | ||
| 30 | enter_time: 3, // tDP = 30uS | ||
| 31 | exit_time: 3, // tRDP = 35uS | ||
| 32 | }); | ||
| 33 | |||
| 34 | let mut q: qspi::Qspi<_, 67108864> = qspi::Qspi::new( | ||
| 35 | &mut p.QSPI, | ||
| 36 | &mut irq, | ||
| 37 | &mut p.P0_19, | ||
| 38 | &mut p.P0_17, | ||
| 39 | &mut p.P0_20, | ||
| 40 | &mut p.P0_21, | ||
| 41 | &mut p.P0_22, | ||
| 42 | &mut p.P0_23, | ||
| 43 | config, | ||
| 44 | ); | ||
| 45 | |||
| 46 | let mut id = [1; 3]; | ||
| 47 | unwrap!(q.custom_instruction(0x9F, &[], &mut id).await); | ||
| 48 | info!("id: {}", id); | ||
| 49 | |||
| 50 | // Read status register | ||
| 51 | let mut status = [4; 1]; | ||
| 52 | unwrap!(q.custom_instruction(0x05, &[], &mut status).await); | ||
| 53 | |||
| 54 | info!("status: {:?}", status[0]); | ||
| 55 | |||
| 56 | if status[0] & 0x40 == 0 { | ||
| 57 | status[0] |= 0x40; | ||
| 58 | |||
| 59 | unwrap!(q.custom_instruction(0x01, &status, &mut []).await); | ||
| 60 | |||
| 61 | info!("enabled quad in status"); | ||
| 62 | } | ||
| 63 | |||
| 64 | let mut buf = AlignedBuf([0u8; 64]); | ||
| 65 | |||
| 66 | info!("reading..."); | ||
| 67 | unwrap!(q.read(0, &mut buf.0).await); | ||
| 68 | info!("read: {=[u8]:x}", buf.0); | ||
| 69 | |||
| 70 | // Drop the QSPI instance. This disables the peripehral and deconfigures the pins. | ||
| 71 | // This clears the borrow on the singletons, so they can now be used again. | ||
| 72 | mem::drop(q); | ||
| 73 | |||
| 74 | // Sleep for 1 second. The executor ensures the core sleeps with a WFE when it has nothing to do. | ||
| 75 | // During this sleep, the nRF chip should only use ~3uA | ||
| 76 | Timer::after(Duration::from_secs(1)).await; | ||
| 77 | } | ||
| 78 | } | ||
diff --git a/examples/nrf52840/src/bin/raw_spawn.rs b/examples/nrf52840/src/bin/raw_spawn.rs new file mode 100644 index 000000000..1b067f5e4 --- /dev/null +++ b/examples/nrf52840/src/bin/raw_spawn.rs | |||
| @@ -0,0 +1,52 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | |||
| 4 | use core::mem; | ||
| 5 | |||
| 6 | use cortex_m_rt::entry; | ||
| 7 | use defmt::{info, unwrap}; | ||
| 8 | use embassy_executor::raw::TaskStorage; | ||
| 9 | use embassy_executor::Executor; | ||
| 10 | use embassy_time::{Duration, Timer}; | ||
| 11 | use static_cell::StaticCell; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | async fn run1() { | ||
| 15 | loop { | ||
| 16 | info!("BIG INFREQUENT TICK"); | ||
| 17 | Timer::after(Duration::from_ticks(64000)).await; | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | async fn run2() { | ||
| 22 | loop { | ||
| 23 | info!("tick"); | ||
| 24 | Timer::after(Duration::from_ticks(13000)).await; | ||
| 25 | } | ||
| 26 | } | ||
| 27 | |||
| 28 | static EXECUTOR: StaticCell<Executor> = StaticCell::new(); | ||
| 29 | |||
| 30 | #[entry] | ||
| 31 | fn main() -> ! { | ||
| 32 | info!("Hello World!"); | ||
| 33 | |||
| 34 | let _p = embassy_nrf::init(Default::default()); | ||
| 35 | let executor = EXECUTOR.init(Executor::new()); | ||
| 36 | |||
| 37 | let run1_task = TaskStorage::new(); | ||
| 38 | let run2_task = TaskStorage::new(); | ||
| 39 | |||
| 40 | // Safety: these variables do live forever if main never returns. | ||
| 41 | let run1_task = unsafe { make_static(&run1_task) }; | ||
| 42 | let run2_task = unsafe { make_static(&run2_task) }; | ||
| 43 | |||
| 44 | executor.run(|spawner| { | ||
| 45 | unwrap!(spawner.spawn(run1_task.spawn(|| run1()))); | ||
| 46 | unwrap!(spawner.spawn(run2_task.spawn(|| run2()))); | ||
| 47 | }); | ||
| 48 | } | ||
| 49 | |||
| 50 | unsafe fn make_static<T>(t: &T) -> &'static T { | ||
| 51 | mem::transmute(t) | ||
| 52 | } | ||
diff --git a/examples/nrf52840/src/bin/rng.rs b/examples/nrf52840/src/bin/rng.rs new file mode 100644 index 000000000..647073949 --- /dev/null +++ b/examples/nrf52840/src/bin/rng.rs | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use embassy_executor::Spawner; | ||
| 6 | use embassy_nrf::interrupt; | ||
| 7 | use embassy_nrf::rng::Rng; | ||
| 8 | use rand::Rng as _; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_nrf::init(Default::default()); | ||
| 14 | let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); | ||
| 15 | |||
| 16 | // Async API | ||
| 17 | let mut bytes = [0; 4]; | ||
| 18 | rng.fill_bytes(&mut bytes).await; | ||
| 19 | defmt::info!("Some random bytes: {:?}", bytes); | ||
| 20 | |||
| 21 | // Sync API with `rand` | ||
| 22 | defmt::info!("A random number from 1 to 10: {:?}", rng.gen_range(1..=10)); | ||
| 23 | |||
| 24 | let mut bytes = [0; 1024]; | ||
| 25 | rng.fill_bytes(&mut bytes).await; | ||
| 26 | let zero_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_zeros()); | ||
| 27 | let one_count: u32 = bytes.iter().fold(0, |acc, val| acc + val.count_ones()); | ||
| 28 | defmt::info!("Chance of zero: {}%", zero_count * 100 / (bytes.len() as u32 * 8)); | ||
| 29 | defmt::info!("Chance of one: {}%", one_count * 100 / (bytes.len() as u32 * 8)); | ||
| 30 | } | ||
diff --git a/examples/nrf52840/src/bin/saadc.rs b/examples/nrf52840/src/bin/saadc.rs new file mode 100644 index 000000000..7cf588090 --- /dev/null +++ b/examples/nrf52840/src/bin/saadc.rs | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::saadc::{ChannelConfig, Config, Saadc}; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_p: Spawner) { | ||
| 14 | let mut p = embassy_nrf::init(Default::default()); | ||
| 15 | let config = Config::default(); | ||
| 16 | let channel_config = ChannelConfig::single_ended(&mut p.P0_02); | ||
| 17 | let mut saadc = Saadc::new(p.SAADC, interrupt::take!(SAADC), config, [channel_config]); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | let mut buf = [0; 1]; | ||
| 21 | saadc.sample(&mut buf).await; | ||
| 22 | info!("sample: {=i16}", &buf[0]); | ||
| 23 | Timer::after(Duration::from_millis(100)).await; | ||
| 24 | } | ||
| 25 | } | ||
diff --git a/examples/nrf52840/src/bin/saadc_continuous.rs b/examples/nrf52840/src/bin/saadc_continuous.rs new file mode 100644 index 000000000..bb50ac65e --- /dev/null +++ b/examples/nrf52840/src/bin/saadc_continuous.rs | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::saadc::{ChannelConfig, Config, Saadc, SamplerState}; | ||
| 9 | use embassy_nrf::timer::Frequency; | ||
| 10 | use embassy_time::Duration; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | // Demonstrates both continuous sampling and scanning multiple channels driven by a PPI linked timer | ||
| 14 | |||
| 15 | #[embassy_executor::main] | ||
| 16 | async fn main(_p: Spawner) { | ||
| 17 | let mut p = embassy_nrf::init(Default::default()); | ||
| 18 | let config = Config::default(); | ||
| 19 | let channel_1_config = ChannelConfig::single_ended(&mut p.P0_02); | ||
| 20 | let channel_2_config = ChannelConfig::single_ended(&mut p.P0_03); | ||
| 21 | let channel_3_config = ChannelConfig::single_ended(&mut p.P0_04); | ||
| 22 | let mut saadc = Saadc::new( | ||
| 23 | p.SAADC, | ||
| 24 | interrupt::take!(SAADC), | ||
| 25 | config, | ||
| 26 | [channel_1_config, channel_2_config, channel_3_config], | ||
| 27 | ); | ||
| 28 | |||
| 29 | // This delay demonstrates that starting the timer prior to running | ||
| 30 | // the task sampler is benign given the calibration that follows. | ||
| 31 | embassy_time::Timer::after(Duration::from_millis(500)).await; | ||
| 32 | saadc.calibrate().await; | ||
| 33 | |||
| 34 | let mut bufs = [[[0; 3]; 500]; 2]; | ||
| 35 | |||
| 36 | let mut c = 0; | ||
| 37 | let mut a: i32 = 0; | ||
| 38 | |||
| 39 | saadc | ||
| 40 | .run_task_sampler( | ||
| 41 | &mut p.TIMER0, | ||
| 42 | &mut p.PPI_CH0, | ||
| 43 | &mut p.PPI_CH1, | ||
| 44 | Frequency::F1MHz, | ||
| 45 | 1000, // We want to sample at 1KHz | ||
| 46 | &mut bufs, | ||
| 47 | move |buf| { | ||
| 48 | // NOTE: It is important that the time spent within this callback | ||
| 49 | // does not exceed the time taken to acquire the 1500 samples we | ||
| 50 | // have in this example, which would be 10us + 2us per | ||
| 51 | // sample * 1500 = 18ms. You need to measure the time taken here | ||
| 52 | // and set the sample buffer size accordingly. Exceeding this | ||
| 53 | // time can lead to the peripheral re-writing the other buffer. | ||
| 54 | for b in buf { | ||
| 55 | a += b[0] as i32; | ||
| 56 | } | ||
| 57 | c += buf.len(); | ||
| 58 | if c > 1000 { | ||
| 59 | a = a / c as i32; | ||
| 60 | info!("channel 1: {=i32}", a); | ||
| 61 | c = 0; | ||
| 62 | a = 0; | ||
| 63 | } | ||
| 64 | SamplerState::Sampled | ||
| 65 | }, | ||
| 66 | ) | ||
| 67 | .await; | ||
| 68 | } | ||
diff --git a/examples/nrf52840/src/bin/self_spawn.rs b/examples/nrf52840/src/bin/self_spawn.rs new file mode 100644 index 000000000..196255a52 --- /dev/null +++ b/examples/nrf52840/src/bin/self_spawn.rs | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_time::{Duration, Timer}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::task(pool_size = 2)] | ||
| 11 | async fn my_task(spawner: Spawner, n: u32) { | ||
| 12 | Timer::after(Duration::from_secs(1)).await; | ||
| 13 | info!("Spawning self! {}", n); | ||
| 14 | unwrap!(spawner.spawn(my_task(spawner, n + 1))); | ||
| 15 | } | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(spawner: Spawner) { | ||
| 19 | let _p = embassy_nrf::init(Default::default()); | ||
| 20 | info!("Hello World!"); | ||
| 21 | unwrap!(spawner.spawn(my_task(spawner, 0))); | ||
| 22 | } | ||
diff --git a/examples/nrf52840/src/bin/self_spawn_current_executor.rs b/examples/nrf52840/src/bin/self_spawn_current_executor.rs new file mode 100644 index 000000000..8a179886c --- /dev/null +++ b/examples/nrf52840/src/bin/self_spawn_current_executor.rs | |||
| @@ -0,0 +1,22 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_time::{Duration, Timer}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::task(pool_size = 2)] | ||
| 11 | async fn my_task(n: u32) { | ||
| 12 | Timer::after(Duration::from_secs(1)).await; | ||
| 13 | info!("Spawning self! {}", n); | ||
| 14 | unwrap!(Spawner::for_current_executor().await.spawn(my_task(n + 1))); | ||
| 15 | } | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(spawner: Spawner) { | ||
| 19 | let _p = embassy_nrf::init(Default::default()); | ||
| 20 | info!("Hello World!"); | ||
| 21 | unwrap!(spawner.spawn(my_task(0))); | ||
| 22 | } | ||
diff --git a/examples/nrf52840/src/bin/spim.rs b/examples/nrf52840/src/bin/spim.rs new file mode 100644 index 000000000..132e01660 --- /dev/null +++ b/examples/nrf52840/src/bin/spim.rs | |||
| @@ -0,0 +1,68 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::gpio::{Level, Output, OutputDrive}; | ||
| 8 | use embassy_nrf::{interrupt, spim}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_nrf::init(Default::default()); | ||
| 14 | info!("running!"); | ||
| 15 | |||
| 16 | let mut config = spim::Config::default(); | ||
| 17 | config.frequency = spim::Frequency::M16; | ||
| 18 | |||
| 19 | let irq = interrupt::take!(SPIM3); | ||
| 20 | let mut spim = spim::Spim::new(p.SPI3, irq, p.P0_29, p.P0_28, p.P0_30, config); | ||
| 21 | |||
| 22 | let mut ncs = Output::new(p.P0_31, Level::High, OutputDrive::Standard); | ||
| 23 | |||
| 24 | // Example on how to talk to an ENC28J60 chip | ||
| 25 | |||
| 26 | // softreset | ||
| 27 | cortex_m::asm::delay(10); | ||
| 28 | ncs.set_low(); | ||
| 29 | cortex_m::asm::delay(5); | ||
| 30 | let tx = [0xFF]; | ||
| 31 | unwrap!(spim.transfer(&mut [], &tx).await); | ||
| 32 | cortex_m::asm::delay(10); | ||
| 33 | ncs.set_high(); | ||
| 34 | |||
| 35 | cortex_m::asm::delay(100000); | ||
| 36 | |||
| 37 | let mut rx = [0; 2]; | ||
| 38 | |||
| 39 | // read ESTAT | ||
| 40 | cortex_m::asm::delay(5000); | ||
| 41 | ncs.set_low(); | ||
| 42 | cortex_m::asm::delay(5000); | ||
| 43 | let tx = [0b000_11101, 0]; | ||
| 44 | unwrap!(spim.transfer(&mut rx, &tx).await); | ||
| 45 | cortex_m::asm::delay(5000); | ||
| 46 | ncs.set_high(); | ||
| 47 | info!("estat: {=[?]}", rx); | ||
| 48 | |||
| 49 | // Switch to bank 3 | ||
| 50 | cortex_m::asm::delay(10); | ||
| 51 | ncs.set_low(); | ||
| 52 | cortex_m::asm::delay(5); | ||
| 53 | let tx = [0b100_11111, 0b11]; | ||
| 54 | unwrap!(spim.transfer(&mut rx, &tx).await); | ||
| 55 | cortex_m::asm::delay(10); | ||
| 56 | ncs.set_high(); | ||
| 57 | |||
| 58 | // read EREVID | ||
| 59 | cortex_m::asm::delay(10); | ||
| 60 | ncs.set_low(); | ||
| 61 | cortex_m::asm::delay(5); | ||
| 62 | let tx = [0b000_10010, 0]; | ||
| 63 | unwrap!(spim.transfer(&mut rx, &tx).await); | ||
| 64 | cortex_m::asm::delay(10); | ||
| 65 | ncs.set_high(); | ||
| 66 | |||
| 67 | info!("erevid: {=[?]}", rx); | ||
| 68 | } | ||
diff --git a/examples/nrf52840/src/bin/spis.rs b/examples/nrf52840/src/bin/spis.rs new file mode 100644 index 000000000..fe3b0c53d --- /dev/null +++ b/examples/nrf52840/src/bin/spis.rs | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::spis::{Config, Spis}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_nrf::init(Default::default()); | ||
| 14 | info!("Running!"); | ||
| 15 | |||
| 16 | let irq = interrupt::take!(SPIM2_SPIS2_SPI2); | ||
| 17 | let mut spis = Spis::new(p.SPI2, irq, p.P0_31, p.P0_29, p.P0_28, p.P0_30, Config::default()); | ||
| 18 | |||
| 19 | loop { | ||
| 20 | let mut rx_buf = [0_u8; 64]; | ||
| 21 | let tx_buf = [1_u8, 2, 3, 4, 5, 6, 7, 8]; | ||
| 22 | if let Ok((n_rx, n_tx)) = spis.transfer(&mut rx_buf, &tx_buf).await { | ||
| 23 | info!("RX: {:?}", rx_buf[..n_rx]); | ||
| 24 | info!("TX: {:?}", tx_buf[..n_tx]); | ||
| 25 | } | ||
| 26 | } | ||
| 27 | } | ||
diff --git a/examples/nrf52840/src/bin/temp.rs b/examples/nrf52840/src/bin/temp.rs new file mode 100644 index 000000000..b06ac709e --- /dev/null +++ b/examples/nrf52840/src/bin/temp.rs | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::info; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::interrupt; | ||
| 8 | use embassy_nrf::temp::Temp; | ||
| 9 | use embassy_time::{Duration, Timer}; | ||
| 10 | use {defmt_rtt as _, panic_probe as _}; | ||
| 11 | |||
| 12 | #[embassy_executor::main] | ||
| 13 | async fn main(_spawner: Spawner) { | ||
| 14 | let p = embassy_nrf::init(Default::default()); | ||
| 15 | let irq = interrupt::take!(TEMP); | ||
| 16 | let mut temp = Temp::new(p.TEMP, irq); | ||
| 17 | |||
| 18 | loop { | ||
| 19 | let value = temp.read().await; | ||
| 20 | info!("temperature: {}℃", value.to_num::<u16>()); | ||
| 21 | Timer::after(Duration::from_secs(1)).await; | ||
| 22 | } | ||
| 23 | } | ||
diff --git a/examples/nrf52840/src/bin/timer.rs b/examples/nrf52840/src/bin/timer.rs new file mode 100644 index 000000000..c22b5acd5 --- /dev/null +++ b/examples/nrf52840/src/bin/timer.rs | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::{info, unwrap}; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_time::{Duration, Timer}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::task] | ||
| 11 | async fn run1() { | ||
| 12 | loop { | ||
| 13 | info!("BIG INFREQUENT TICK"); | ||
| 14 | Timer::after(Duration::from_ticks(64000)).await; | ||
| 15 | } | ||
| 16 | } | ||
| 17 | |||
| 18 | #[embassy_executor::task] | ||
| 19 | async fn run2() { | ||
| 20 | loop { | ||
| 21 | info!("tick"); | ||
| 22 | Timer::after(Duration::from_ticks(13000)).await; | ||
| 23 | } | ||
| 24 | } | ||
| 25 | |||
| 26 | #[embassy_executor::main] | ||
| 27 | async fn main(spawner: Spawner) { | ||
| 28 | let _p = embassy_nrf::init(Default::default()); | ||
| 29 | unwrap!(spawner.spawn(run1())); | ||
| 30 | unwrap!(spawner.spawn(run2())); | ||
| 31 | } | ||
diff --git a/examples/nrf52840/src/bin/twim.rs b/examples/nrf52840/src/bin/twim.rs new file mode 100644 index 000000000..a027cc1e7 --- /dev/null +++ b/examples/nrf52840/src/bin/twim.rs | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | //! Example on how to read a 24C/24LC i2c eeprom. | ||
| 2 | //! | ||
| 3 | //! Connect SDA to P0.03, SCL to P0.04 | ||
| 4 | |||
| 5 | #![no_std] | ||
| 6 | #![no_main] | ||
| 7 | #![feature(type_alias_impl_trait)] | ||
| 8 | |||
| 9 | use defmt::*; | ||
| 10 | use embassy_executor::Spawner; | ||
| 11 | use embassy_nrf::interrupt; | ||
| 12 | use embassy_nrf::twim::{self, Twim}; | ||
| 13 | use {defmt_rtt as _, panic_probe as _}; | ||
| 14 | |||
| 15 | const ADDRESS: u8 = 0x50; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_nrf::init(Default::default()); | ||
| 20 | info!("Initializing TWI..."); | ||
| 21 | let config = twim::Config::default(); | ||
| 22 | let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); | ||
| 23 | let mut twi = Twim::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); | ||
| 24 | |||
| 25 | info!("Reading..."); | ||
| 26 | |||
| 27 | let mut buf = [0u8; 16]; | ||
| 28 | unwrap!(twi.blocking_write_read(ADDRESS, &mut [0x00], &mut buf)); | ||
| 29 | |||
| 30 | info!("Read: {=[u8]:x}", buf); | ||
| 31 | } | ||
diff --git a/examples/nrf52840/src/bin/twim_lowpower.rs b/examples/nrf52840/src/bin/twim_lowpower.rs new file mode 100644 index 000000000..e30cc9688 --- /dev/null +++ b/examples/nrf52840/src/bin/twim_lowpower.rs | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | //! Example on how to read a 24C/24LC i2c eeprom with low power consumption. | ||
| 2 | //! The eeprom is read every 1 second, while ensuring lowest possible power while | ||
| 3 | //! sleeping between reads. | ||
| 4 | //! | ||
| 5 | //! Connect SDA to P0.03, SCL to P0.04 | ||
| 6 | |||
| 7 | #![no_std] | ||
| 8 | #![no_main] | ||
| 9 | #![feature(type_alias_impl_trait)] | ||
| 10 | |||
| 11 | use core::mem; | ||
| 12 | |||
| 13 | use defmt::*; | ||
| 14 | use embassy_executor::Spawner; | ||
| 15 | use embassy_nrf::interrupt; | ||
| 16 | use embassy_nrf::twim::{self, Twim}; | ||
| 17 | use embassy_time::{Duration, Timer}; | ||
| 18 | use {defmt_rtt as _, panic_probe as _}; | ||
| 19 | |||
| 20 | const ADDRESS: u8 = 0x50; | ||
| 21 | |||
| 22 | #[embassy_executor::main] | ||
| 23 | async fn main(_p: Spawner) { | ||
| 24 | let mut p = embassy_nrf::init(Default::default()); | ||
| 25 | info!("Started!"); | ||
| 26 | let mut irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); | ||
| 27 | |||
| 28 | loop { | ||
| 29 | info!("Initializing TWI..."); | ||
| 30 | let config = twim::Config::default(); | ||
| 31 | |||
| 32 | // Create the TWIM instance with borrowed singletons, so they're not consumed. | ||
| 33 | let mut twi = Twim::new(&mut p.TWISPI0, &mut irq, &mut p.P0_03, &mut p.P0_04, config); | ||
| 34 | |||
| 35 | info!("Reading..."); | ||
| 36 | |||
| 37 | let mut buf = [0u8; 16]; | ||
| 38 | unwrap!(twi.blocking_write_read(ADDRESS, &mut [0x00], &mut buf)); | ||
| 39 | |||
| 40 | info!("Read: {=[u8]:x}", buf); | ||
| 41 | |||
| 42 | // Drop the TWIM instance. This disables the peripehral and deconfigures the pins. | ||
| 43 | // This clears the borrow on the singletons, so they can now be used again. | ||
| 44 | mem::drop(twi); | ||
| 45 | |||
| 46 | // Sleep for 1 second. The executor ensures the core sleeps with a WFE when it has nothing to do. | ||
| 47 | // During this sleep, the nRF chip should only use ~3uA | ||
| 48 | Timer::after(Duration::from_secs(1)).await; | ||
| 49 | } | ||
| 50 | } | ||
diff --git a/examples/nrf52840/src/bin/twis.rs b/examples/nrf52840/src/bin/twis.rs new file mode 100644 index 000000000..54cba9494 --- /dev/null +++ b/examples/nrf52840/src/bin/twis.rs | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | //! TWIS example | ||
| 2 | |||
| 3 | #![no_std] | ||
| 4 | #![no_main] | ||
| 5 | #![feature(type_alias_impl_trait)] | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::interrupt; | ||
| 10 | use embassy_nrf::twis::{self, Command, Twis}; | ||
| 11 | use {defmt_rtt as _, panic_probe as _}; | ||
| 12 | |||
| 13 | #[embassy_executor::main] | ||
| 14 | async fn main(_spawner: Spawner) { | ||
| 15 | let p = embassy_nrf::init(Default::default()); | ||
| 16 | |||
| 17 | let irq = interrupt::take!(SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0); | ||
| 18 | let mut config = twis::Config::default(); | ||
| 19 | // Set i2c address | ||
| 20 | config.address0 = 0x55; | ||
| 21 | let mut i2c = Twis::new(p.TWISPI0, irq, p.P0_03, p.P0_04, config); | ||
| 22 | |||
| 23 | info!("Listening..."); | ||
| 24 | loop { | ||
| 25 | let response = [1, 2, 3, 4, 5, 6, 7, 8]; | ||
| 26 | // This buffer is used if the i2c master performs a Write or WriteRead | ||
| 27 | let mut buf = [0u8; 16]; | ||
| 28 | match i2c.listen(&mut buf).await { | ||
| 29 | Ok(Command::Read) => { | ||
| 30 | info!("Got READ command. Respond with data:\n{:?}\n", response); | ||
| 31 | if let Err(e) = i2c.respond_to_read(&response).await { | ||
| 32 | error!("{:?}", e); | ||
| 33 | } | ||
| 34 | } | ||
| 35 | Ok(Command::Write(n)) => info!("Got WRITE command with data:\n{:?}\n", buf[..n]), | ||
| 36 | Ok(Command::WriteRead(n)) => { | ||
| 37 | info!("Got WRITE/READ command with data:\n{:?}", buf[..n]); | ||
| 38 | info!("Respond with data:\n{:?}\n", response); | ||
| 39 | if let Err(e) = i2c.respond_to_read(&response).await { | ||
| 40 | error!("{:?}", e); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | Err(e) => error!("{:?}", e), | ||
| 44 | } | ||
| 45 | } | ||
| 46 | } | ||
diff --git a/examples/nrf52840/src/bin/uart.rs b/examples/nrf52840/src/bin/uart.rs new file mode 100644 index 000000000..600f7a6ef --- /dev/null +++ b/examples/nrf52840/src/bin/uart.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::{interrupt, uarte}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) { | ||
| 12 | let p = embassy_nrf::init(Default::default()); | ||
| 13 | let mut config = uarte::Config::default(); | ||
| 14 | config.parity = uarte::Parity::EXCLUDED; | ||
| 15 | config.baudrate = uarte::Baudrate::BAUD115200; | ||
| 16 | |||
| 17 | let irq = interrupt::take!(UARTE0_UART0); | ||
| 18 | let mut uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); | ||
| 19 | |||
| 20 | info!("uarte initialized!"); | ||
| 21 | |||
| 22 | // Message must be in SRAM | ||
| 23 | let mut buf = [0; 8]; | ||
| 24 | buf.copy_from_slice(b"Hello!\r\n"); | ||
| 25 | |||
| 26 | unwrap!(uart.write(&buf).await); | ||
| 27 | info!("wrote hello in uart!"); | ||
| 28 | |||
| 29 | loop { | ||
| 30 | info!("reading..."); | ||
| 31 | unwrap!(uart.read(&mut buf).await); | ||
| 32 | info!("writing..."); | ||
| 33 | unwrap!(uart.write(&buf).await); | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/examples/nrf52840/src/bin/uart_idle.rs b/examples/nrf52840/src/bin/uart_idle.rs new file mode 100644 index 000000000..6af4f7097 --- /dev/null +++ b/examples/nrf52840/src/bin/uart_idle.rs | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::{interrupt, uarte}; | ||
| 8 | use {defmt_rtt as _, panic_probe as _}; | ||
| 9 | |||
| 10 | #[embassy_executor::main] | ||
| 11 | async fn main(_spawner: Spawner) { | ||
| 12 | let p = embassy_nrf::init(Default::default()); | ||
| 13 | let mut config = uarte::Config::default(); | ||
| 14 | config.parity = uarte::Parity::EXCLUDED; | ||
| 15 | config.baudrate = uarte::Baudrate::BAUD115200; | ||
| 16 | |||
| 17 | let irq = interrupt::take!(UARTE0_UART0); | ||
| 18 | let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); | ||
| 19 | let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); | ||
| 20 | |||
| 21 | info!("uarte initialized!"); | ||
| 22 | |||
| 23 | // Message must be in SRAM | ||
| 24 | let mut buf = [0; 8]; | ||
| 25 | buf.copy_from_slice(b"Hello!\r\n"); | ||
| 26 | |||
| 27 | unwrap!(tx.write(&buf).await); | ||
| 28 | info!("wrote hello in uart!"); | ||
| 29 | |||
| 30 | loop { | ||
| 31 | info!("reading..."); | ||
| 32 | let n = unwrap!(rx.read_until_idle(&mut buf).await); | ||
| 33 | info!("got {} bytes", n); | ||
| 34 | } | ||
| 35 | } | ||
diff --git a/examples/nrf52840/src/bin/uart_split.rs b/examples/nrf52840/src/bin/uart_split.rs new file mode 100644 index 000000000..1adaf53fd --- /dev/null +++ b/examples/nrf52840/src/bin/uart_split.rs | |||
| @@ -0,0 +1,60 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::peripherals::UARTE0; | ||
| 8 | use embassy_nrf::uarte::UarteRx; | ||
| 9 | use embassy_nrf::{interrupt, uarte}; | ||
| 10 | use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; | ||
| 11 | use embassy_sync::channel::Channel; | ||
| 12 | use {defmt_rtt as _, panic_probe as _}; | ||
| 13 | |||
| 14 | static CHANNEL: Channel<ThreadModeRawMutex, [u8; 8], 1> = Channel::new(); | ||
| 15 | |||
| 16 | #[embassy_executor::main] | ||
| 17 | async fn main(spawner: Spawner) { | ||
| 18 | let p = embassy_nrf::init(Default::default()); | ||
| 19 | let mut config = uarte::Config::default(); | ||
| 20 | config.parity = uarte::Parity::EXCLUDED; | ||
| 21 | config.baudrate = uarte::Baudrate::BAUD115200; | ||
| 22 | |||
| 23 | let irq = interrupt::take!(UARTE0_UART0); | ||
| 24 | let uart = uarte::Uarte::new(p.UARTE0, irq, p.P0_08, p.P0_06, config); | ||
| 25 | let (mut tx, rx) = uart.split(); | ||
| 26 | |||
| 27 | info!("uarte initialized!"); | ||
| 28 | |||
| 29 | // Spawn a task responsible purely for reading | ||
| 30 | |||
| 31 | unwrap!(spawner.spawn(reader(rx))); | ||
| 32 | |||
| 33 | // Message must be in SRAM | ||
| 34 | { | ||
| 35 | let mut buf = [0; 23]; | ||
| 36 | buf.copy_from_slice(b"Type 8 chars to echo!\r\n"); | ||
| 37 | |||
| 38 | unwrap!(tx.write(&buf).await); | ||
| 39 | info!("wrote hello in uart!"); | ||
| 40 | } | ||
| 41 | |||
| 42 | // Continue reading in this main task and write | ||
| 43 | // back out the buffer we receive from the read | ||
| 44 | // task. | ||
| 45 | loop { | ||
| 46 | let buf = CHANNEL.recv().await; | ||
| 47 | info!("writing..."); | ||
| 48 | unwrap!(tx.write(&buf).await); | ||
| 49 | } | ||
| 50 | } | ||
| 51 | |||
| 52 | #[embassy_executor::task] | ||
| 53 | async fn reader(mut rx: UarteRx<'static, UARTE0>) { | ||
| 54 | let mut buf = [0; 8]; | ||
| 55 | loop { | ||
| 56 | info!("reading..."); | ||
| 57 | unwrap!(rx.read(&mut buf).await); | ||
| 58 | CHANNEL.send(buf).await; | ||
| 59 | } | ||
| 60 | } | ||
diff --git a/examples/nrf52840/src/bin/usb_ethernet.rs b/examples/nrf52840/src/bin/usb_ethernet.rs new file mode 100644 index 000000000..e5f704524 --- /dev/null +++ b/examples/nrf52840/src/bin/usb_ethernet.rs | |||
| @@ -0,0 +1,169 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::mem; | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_net::tcp::TcpSocket; | ||
| 10 | use embassy_net::{Stack, StackResources}; | ||
| 11 | use embassy_nrf::rng::Rng; | ||
| 12 | use embassy_nrf::usb::{Driver, PowerUsb}; | ||
| 13 | use embassy_nrf::{interrupt, pac, peripherals}; | ||
| 14 | use embassy_usb::class::cdc_ncm::embassy_net::{Device, Runner, State as NetState}; | ||
| 15 | use embassy_usb::class::cdc_ncm::{CdcNcmClass, State}; | ||
| 16 | use embassy_usb::{Builder, Config, UsbDevice}; | ||
| 17 | use embedded_io::asynch::Write; | ||
| 18 | use static_cell::StaticCell; | ||
| 19 | use {defmt_rtt as _, panic_probe as _}; | ||
| 20 | |||
| 21 | type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; | ||
| 22 | |||
| 23 | macro_rules! singleton { | ||
| 24 | ($val:expr) => {{ | ||
| 25 | type T = impl Sized; | ||
| 26 | static STATIC_CELL: StaticCell<T> = StaticCell::new(); | ||
| 27 | let (x,) = STATIC_CELL.init(($val,)); | ||
| 28 | x | ||
| 29 | }}; | ||
| 30 | } | ||
| 31 | |||
| 32 | const MTU: usize = 1514; | ||
| 33 | |||
| 34 | #[embassy_executor::task] | ||
| 35 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) -> ! { | ||
| 36 | device.run().await | ||
| 37 | } | ||
| 38 | |||
| 39 | #[embassy_executor::task] | ||
| 40 | async fn usb_ncm_task(class: Runner<'static, MyDriver, MTU>) -> ! { | ||
| 41 | class.run().await | ||
| 42 | } | ||
| 43 | |||
| 44 | #[embassy_executor::task] | ||
| 45 | async fn net_task(stack: &'static Stack<Device<'static, MTU>>) -> ! { | ||
| 46 | stack.run().await | ||
| 47 | } | ||
| 48 | |||
| 49 | #[embassy_executor::main] | ||
| 50 | async fn main(spawner: Spawner) { | ||
| 51 | let p = embassy_nrf::init(Default::default()); | ||
| 52 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 53 | |||
| 54 | info!("Enabling ext hfosc..."); | ||
| 55 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 56 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 57 | |||
| 58 | // Create the driver, from the HAL. | ||
| 59 | let irq = interrupt::take!(USBD); | ||
| 60 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 61 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 62 | |||
| 63 | // Create embassy-usb Config | ||
| 64 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 65 | config.manufacturer = Some("Embassy"); | ||
| 66 | config.product = Some("USB-Ethernet example"); | ||
| 67 | config.serial_number = Some("12345678"); | ||
| 68 | config.max_power = 100; | ||
| 69 | config.max_packet_size_0 = 64; | ||
| 70 | |||
| 71 | // Required for Windows support. | ||
| 72 | config.composite_with_iads = true; | ||
| 73 | config.device_class = 0xEF; | ||
| 74 | config.device_sub_class = 0x02; | ||
| 75 | config.device_protocol = 0x01; | ||
| 76 | |||
| 77 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 78 | let mut builder = Builder::new( | ||
| 79 | driver, | ||
| 80 | config, | ||
| 81 | &mut singleton!([0; 256])[..], | ||
| 82 | &mut singleton!([0; 256])[..], | ||
| 83 | &mut singleton!([0; 256])[..], | ||
| 84 | &mut singleton!([0; 128])[..], | ||
| 85 | None, | ||
| 86 | ); | ||
| 87 | |||
| 88 | // Our MAC addr. | ||
| 89 | let our_mac_addr = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]; | ||
| 90 | // Host's MAC addr. This is the MAC the host "thinks" its USB-to-ethernet adapter has. | ||
| 91 | let host_mac_addr = [0x88, 0x88, 0x88, 0x88, 0x88, 0x88]; | ||
| 92 | |||
| 93 | // Create classes on the builder. | ||
| 94 | let class = CdcNcmClass::new(&mut builder, singleton!(State::new()), host_mac_addr, 64); | ||
| 95 | |||
| 96 | // Build the builder. | ||
| 97 | let usb = builder.build(); | ||
| 98 | |||
| 99 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 100 | |||
| 101 | let (runner, device) = class.into_embassy_net_device::<MTU, 4, 4>(singleton!(NetState::new()), our_mac_addr); | ||
| 102 | unwrap!(spawner.spawn(usb_ncm_task(runner))); | ||
| 103 | |||
| 104 | let config = embassy_net::ConfigStrategy::Dhcp; | ||
| 105 | //let config = embassy_net::ConfigStrategy::Static(embassy_net::Config { | ||
| 106 | // address: Ipv4Cidr::new(Ipv4Address::new(10, 42, 0, 61), 24), | ||
| 107 | // dns_servers: Vec::new(), | ||
| 108 | // gateway: Some(Ipv4Address::new(10, 42, 0, 1)), | ||
| 109 | //}); | ||
| 110 | |||
| 111 | // Generate random seed | ||
| 112 | let mut rng = Rng::new(p.RNG, interrupt::take!(RNG)); | ||
| 113 | let mut seed = [0; 8]; | ||
| 114 | rng.blocking_fill_bytes(&mut seed); | ||
| 115 | let seed = u64::from_le_bytes(seed); | ||
| 116 | |||
| 117 | // Init network stack | ||
| 118 | let stack = &*singleton!(Stack::new( | ||
| 119 | device, | ||
| 120 | config, | ||
| 121 | singleton!(StackResources::<1, 2, 8>::new()), | ||
| 122 | seed | ||
| 123 | )); | ||
| 124 | |||
| 125 | unwrap!(spawner.spawn(net_task(stack))); | ||
| 126 | |||
| 127 | // And now we can use it! | ||
| 128 | |||
| 129 | let mut rx_buffer = [0; 4096]; | ||
| 130 | let mut tx_buffer = [0; 4096]; | ||
| 131 | let mut buf = [0; 4096]; | ||
| 132 | |||
| 133 | loop { | ||
| 134 | let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer); | ||
| 135 | socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10))); | ||
| 136 | |||
| 137 | info!("Listening on TCP:1234..."); | ||
| 138 | if let Err(e) = socket.accept(1234).await { | ||
| 139 | warn!("accept error: {:?}", e); | ||
| 140 | continue; | ||
| 141 | } | ||
| 142 | |||
| 143 | info!("Received connection from {:?}", socket.remote_endpoint()); | ||
| 144 | |||
| 145 | loop { | ||
| 146 | let n = match socket.read(&mut buf).await { | ||
| 147 | Ok(0) => { | ||
| 148 | warn!("read EOF"); | ||
| 149 | break; | ||
| 150 | } | ||
| 151 | Ok(n) => n, | ||
| 152 | Err(e) => { | ||
| 153 | warn!("read error: {:?}", e); | ||
| 154 | break; | ||
| 155 | } | ||
| 156 | }; | ||
| 157 | |||
| 158 | info!("rxd {:02x}", &buf[..n]); | ||
| 159 | |||
| 160 | match socket.write_all(&buf[..n]).await { | ||
| 161 | Ok(()) => {} | ||
| 162 | Err(e) => { | ||
| 163 | warn!("write error: {:?}", e); | ||
| 164 | break; | ||
| 165 | } | ||
| 166 | }; | ||
| 167 | } | ||
| 168 | } | ||
| 169 | } | ||
diff --git a/examples/nrf52840/src/bin/usb_hid_keyboard.rs b/examples/nrf52840/src/bin/usb_hid_keyboard.rs new file mode 100644 index 000000000..76e198719 --- /dev/null +++ b/examples/nrf52840/src/bin/usb_hid_keyboard.rs | |||
| @@ -0,0 +1,222 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::mem; | ||
| 6 | use core::sync::atomic::{AtomicBool, Ordering}; | ||
| 7 | |||
| 8 | use defmt::*; | ||
| 9 | use embassy_executor::Spawner; | ||
| 10 | use embassy_futures::join::join; | ||
| 11 | use embassy_futures::select::{select, Either}; | ||
| 12 | use embassy_nrf::gpio::{Input, Pin, Pull}; | ||
| 13 | use embassy_nrf::usb::{Driver, PowerUsb}; | ||
| 14 | use embassy_nrf::{interrupt, pac}; | ||
| 15 | use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||
| 16 | use embassy_sync::signal::Signal; | ||
| 17 | use embassy_usb::class::hid::{HidReaderWriter, ReportId, RequestHandler, State}; | ||
| 18 | use embassy_usb::control::OutResponse; | ||
| 19 | use embassy_usb::{Builder, Config, DeviceStateHandler}; | ||
| 20 | use usbd_hid::descriptor::{KeyboardReport, SerializedDescriptor}; | ||
| 21 | use {defmt_rtt as _, panic_probe as _}; | ||
| 22 | |||
| 23 | static SUSPENDED: AtomicBool = AtomicBool::new(false); | ||
| 24 | |||
| 25 | #[embassy_executor::main] | ||
| 26 | async fn main(_spawner: Spawner) { | ||
| 27 | let p = embassy_nrf::init(Default::default()); | ||
| 28 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 29 | |||
| 30 | info!("Enabling ext hfosc..."); | ||
| 31 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 32 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 33 | |||
| 34 | // Create the driver, from the HAL. | ||
| 35 | let irq = interrupt::take!(USBD); | ||
| 36 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 37 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 38 | |||
| 39 | // Create embassy-usb Config | ||
| 40 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 41 | config.manufacturer = Some("Embassy"); | ||
| 42 | config.product = Some("HID keyboard example"); | ||
| 43 | config.serial_number = Some("12345678"); | ||
| 44 | config.max_power = 100; | ||
| 45 | config.max_packet_size_0 = 64; | ||
| 46 | config.supports_remote_wakeup = true; | ||
| 47 | |||
| 48 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 49 | // It needs some buffers for building the descriptors. | ||
| 50 | let mut device_descriptor = [0; 256]; | ||
| 51 | let mut config_descriptor = [0; 256]; | ||
| 52 | let mut bos_descriptor = [0; 256]; | ||
| 53 | let mut control_buf = [0; 64]; | ||
| 54 | let request_handler = MyRequestHandler {}; | ||
| 55 | let device_state_handler = MyDeviceStateHandler::new(); | ||
| 56 | |||
| 57 | let mut state = State::new(); | ||
| 58 | |||
| 59 | let mut builder = Builder::new( | ||
| 60 | driver, | ||
| 61 | config, | ||
| 62 | &mut device_descriptor, | ||
| 63 | &mut config_descriptor, | ||
| 64 | &mut bos_descriptor, | ||
| 65 | &mut control_buf, | ||
| 66 | Some(&device_state_handler), | ||
| 67 | ); | ||
| 68 | |||
| 69 | // Create classes on the builder. | ||
| 70 | let config = embassy_usb::class::hid::Config { | ||
| 71 | report_descriptor: KeyboardReport::desc(), | ||
| 72 | request_handler: Some(&request_handler), | ||
| 73 | poll_ms: 60, | ||
| 74 | max_packet_size: 64, | ||
| 75 | }; | ||
| 76 | let hid = HidReaderWriter::<_, 1, 8>::new(&mut builder, &mut state, config); | ||
| 77 | |||
| 78 | // Build the builder. | ||
| 79 | let mut usb = builder.build(); | ||
| 80 | |||
| 81 | let remote_wakeup: Signal<CriticalSectionRawMutex, _> = Signal::new(); | ||
| 82 | |||
| 83 | // Run the USB device. | ||
| 84 | let usb_fut = async { | ||
| 85 | loop { | ||
| 86 | usb.run_until_suspend().await; | ||
| 87 | match select(usb.wait_resume(), remote_wakeup.wait()).await { | ||
| 88 | Either::First(_) => (), | ||
| 89 | Either::Second(_) => unwrap!(usb.remote_wakeup().await), | ||
| 90 | } | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | |||
| 94 | let mut button = Input::new(p.P0_11.degrade(), Pull::Up); | ||
| 95 | |||
| 96 | let (reader, mut writer) = hid.split(); | ||
| 97 | |||
| 98 | // Do stuff with the class! | ||
| 99 | let in_fut = async { | ||
| 100 | loop { | ||
| 101 | button.wait_for_low().await; | ||
| 102 | info!("PRESSED"); | ||
| 103 | |||
| 104 | if SUSPENDED.load(Ordering::Acquire) { | ||
| 105 | info!("Triggering remote wakeup"); | ||
| 106 | remote_wakeup.signal(()); | ||
| 107 | } else { | ||
| 108 | let report = KeyboardReport { | ||
| 109 | keycodes: [4, 0, 0, 0, 0, 0], | ||
| 110 | leds: 0, | ||
| 111 | modifier: 0, | ||
| 112 | reserved: 0, | ||
| 113 | }; | ||
| 114 | match writer.write_serialize(&report).await { | ||
| 115 | Ok(()) => {} | ||
| 116 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 117 | }; | ||
| 118 | } | ||
| 119 | |||
| 120 | button.wait_for_high().await; | ||
| 121 | info!("RELEASED"); | ||
| 122 | let report = KeyboardReport { | ||
| 123 | keycodes: [0, 0, 0, 0, 0, 0], | ||
| 124 | leds: 0, | ||
| 125 | modifier: 0, | ||
| 126 | reserved: 0, | ||
| 127 | }; | ||
| 128 | match writer.write_serialize(&report).await { | ||
| 129 | Ok(()) => {} | ||
| 130 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 131 | }; | ||
| 132 | } | ||
| 133 | }; | ||
| 134 | |||
| 135 | let out_fut = async { | ||
| 136 | reader.run(false, &request_handler).await; | ||
| 137 | }; | ||
| 138 | |||
| 139 | // Run everything concurrently. | ||
| 140 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 141 | join(usb_fut, join(in_fut, out_fut)).await; | ||
| 142 | } | ||
| 143 | |||
| 144 | struct MyRequestHandler {} | ||
| 145 | |||
| 146 | impl RequestHandler for MyRequestHandler { | ||
| 147 | fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { | ||
| 148 | info!("Get report for {:?}", id); | ||
| 149 | None | ||
| 150 | } | ||
| 151 | |||
| 152 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 153 | info!("Set report for {:?}: {=[u8]}", id, data); | ||
| 154 | OutResponse::Accepted | ||
| 155 | } | ||
| 156 | |||
| 157 | fn set_idle_ms(&self, id: Option<ReportId>, dur: u32) { | ||
| 158 | info!("Set idle rate for {:?} to {:?}", id, dur); | ||
| 159 | } | ||
| 160 | |||
| 161 | fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> { | ||
| 162 | info!("Get idle rate for {:?}", id); | ||
| 163 | None | ||
| 164 | } | ||
| 165 | } | ||
| 166 | |||
| 167 | struct MyDeviceStateHandler { | ||
| 168 | configured: AtomicBool, | ||
| 169 | } | ||
| 170 | |||
| 171 | impl MyDeviceStateHandler { | ||
| 172 | fn new() -> Self { | ||
| 173 | MyDeviceStateHandler { | ||
| 174 | configured: AtomicBool::new(false), | ||
| 175 | } | ||
| 176 | } | ||
| 177 | } | ||
| 178 | |||
| 179 | impl DeviceStateHandler for MyDeviceStateHandler { | ||
| 180 | fn enabled(&self, enabled: bool) { | ||
| 181 | self.configured.store(false, Ordering::Relaxed); | ||
| 182 | SUSPENDED.store(false, Ordering::Release); | ||
| 183 | if enabled { | ||
| 184 | info!("Device enabled"); | ||
| 185 | } else { | ||
| 186 | info!("Device disabled"); | ||
| 187 | } | ||
| 188 | } | ||
| 189 | |||
| 190 | fn reset(&self) { | ||
| 191 | self.configured.store(false, Ordering::Relaxed); | ||
| 192 | info!("Bus reset, the Vbus current limit is 100mA"); | ||
| 193 | } | ||
| 194 | |||
| 195 | fn addressed(&self, addr: u8) { | ||
| 196 | self.configured.store(false, Ordering::Relaxed); | ||
| 197 | info!("USB address set to: {}", addr); | ||
| 198 | } | ||
| 199 | |||
| 200 | fn configured(&self, configured: bool) { | ||
| 201 | self.configured.store(configured, Ordering::Relaxed); | ||
| 202 | if configured { | ||
| 203 | info!("Device configured, it may now draw up to the configured current limit from Vbus.") | ||
| 204 | } else { | ||
| 205 | info!("Device is no longer configured, the Vbus current limit is 100mA."); | ||
| 206 | } | ||
| 207 | } | ||
| 208 | |||
| 209 | fn suspended(&self, suspended: bool) { | ||
| 210 | if suspended { | ||
| 211 | info!("Device suspended, the Vbus current limit is 500µA (or 2.5mA for high-power devices with remote wakeup enabled)."); | ||
| 212 | SUSPENDED.store(true, Ordering::Release); | ||
| 213 | } else { | ||
| 214 | SUSPENDED.store(false, Ordering::Release); | ||
| 215 | if self.configured.load(Ordering::Relaxed) { | ||
| 216 | info!("Device resumed, it may now draw up to the configured current limit from Vbus"); | ||
| 217 | } else { | ||
| 218 | info!("Device resumed, the Vbus current limit is 100mA"); | ||
| 219 | } | ||
| 220 | } | ||
| 221 | } | ||
| 222 | } | ||
diff --git a/examples/nrf52840/src/bin/usb_hid_mouse.rs b/examples/nrf52840/src/bin/usb_hid_mouse.rs new file mode 100644 index 000000000..4916a38d4 --- /dev/null +++ b/examples/nrf52840/src/bin/usb_hid_mouse.rs | |||
| @@ -0,0 +1,124 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::mem; | ||
| 6 | |||
| 7 | use defmt::*; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_futures::join::join; | ||
| 10 | use embassy_nrf::usb::{Driver, PowerUsb}; | ||
| 11 | use embassy_nrf::{interrupt, pac}; | ||
| 12 | use embassy_time::{Duration, Timer}; | ||
| 13 | use embassy_usb::class::hid::{HidWriter, ReportId, RequestHandler, State}; | ||
| 14 | use embassy_usb::control::OutResponse; | ||
| 15 | use embassy_usb::{Builder, Config}; | ||
| 16 | use usbd_hid::descriptor::{MouseReport, SerializedDescriptor}; | ||
| 17 | use {defmt_rtt as _, panic_probe as _}; | ||
| 18 | |||
| 19 | #[embassy_executor::main] | ||
| 20 | async fn main(_spawner: Spawner) { | ||
| 21 | let p = embassy_nrf::init(Default::default()); | ||
| 22 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 23 | |||
| 24 | info!("Enabling ext hfosc..."); | ||
| 25 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 26 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 27 | |||
| 28 | // Create the driver, from the HAL. | ||
| 29 | let irq = interrupt::take!(USBD); | ||
| 30 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 31 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 32 | |||
| 33 | // Create embassy-usb Config | ||
| 34 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 35 | config.manufacturer = Some("Embassy"); | ||
| 36 | config.product = Some("HID mouse example"); | ||
| 37 | config.serial_number = Some("12345678"); | ||
| 38 | config.max_power = 100; | ||
| 39 | config.max_packet_size_0 = 64; | ||
| 40 | |||
| 41 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 42 | // It needs some buffers for building the descriptors. | ||
| 43 | let mut device_descriptor = [0; 256]; | ||
| 44 | let mut config_descriptor = [0; 256]; | ||
| 45 | let mut bos_descriptor = [0; 256]; | ||
| 46 | let mut control_buf = [0; 64]; | ||
| 47 | let request_handler = MyRequestHandler {}; | ||
| 48 | |||
| 49 | let mut state = State::new(); | ||
| 50 | |||
| 51 | let mut builder = Builder::new( | ||
| 52 | driver, | ||
| 53 | config, | ||
| 54 | &mut device_descriptor, | ||
| 55 | &mut config_descriptor, | ||
| 56 | &mut bos_descriptor, | ||
| 57 | &mut control_buf, | ||
| 58 | None, | ||
| 59 | ); | ||
| 60 | |||
| 61 | // Create classes on the builder. | ||
| 62 | let config = embassy_usb::class::hid::Config { | ||
| 63 | report_descriptor: MouseReport::desc(), | ||
| 64 | request_handler: Some(&request_handler), | ||
| 65 | poll_ms: 60, | ||
| 66 | max_packet_size: 8, | ||
| 67 | }; | ||
| 68 | |||
| 69 | let mut writer = HidWriter::<_, 5>::new(&mut builder, &mut state, config); | ||
| 70 | |||
| 71 | // Build the builder. | ||
| 72 | let mut usb = builder.build(); | ||
| 73 | |||
| 74 | // Run the USB device. | ||
| 75 | let usb_fut = usb.run(); | ||
| 76 | |||
| 77 | // Do stuff with the class! | ||
| 78 | let hid_fut = async { | ||
| 79 | let mut y: i8 = 5; | ||
| 80 | loop { | ||
| 81 | Timer::after(Duration::from_millis(500)).await; | ||
| 82 | |||
| 83 | y = -y; | ||
| 84 | let report = MouseReport { | ||
| 85 | buttons: 0, | ||
| 86 | x: 0, | ||
| 87 | y, | ||
| 88 | wheel: 0, | ||
| 89 | pan: 0, | ||
| 90 | }; | ||
| 91 | match writer.write_serialize(&report).await { | ||
| 92 | Ok(()) => {} | ||
| 93 | Err(e) => warn!("Failed to send report: {:?}", e), | ||
| 94 | } | ||
| 95 | } | ||
| 96 | }; | ||
| 97 | |||
| 98 | // Run everything concurrently. | ||
| 99 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 100 | join(usb_fut, hid_fut).await; | ||
| 101 | } | ||
| 102 | |||
| 103 | struct MyRequestHandler {} | ||
| 104 | |||
| 105 | impl RequestHandler for MyRequestHandler { | ||
| 106 | fn get_report(&self, id: ReportId, _buf: &mut [u8]) -> Option<usize> { | ||
| 107 | info!("Get report for {:?}", id); | ||
| 108 | None | ||
| 109 | } | ||
| 110 | |||
| 111 | fn set_report(&self, id: ReportId, data: &[u8]) -> OutResponse { | ||
| 112 | info!("Set report for {:?}: {=[u8]}", id, data); | ||
| 113 | OutResponse::Accepted | ||
| 114 | } | ||
| 115 | |||
| 116 | fn set_idle_ms(&self, id: Option<ReportId>, dur: u32) { | ||
| 117 | info!("Set idle rate for {:?} to {:?}", id, dur); | ||
| 118 | } | ||
| 119 | |||
| 120 | fn get_idle_ms(&self, id: Option<ReportId>) -> Option<u32> { | ||
| 121 | info!("Get idle rate for {:?}", id); | ||
| 122 | None | ||
| 123 | } | ||
| 124 | } | ||
diff --git a/examples/nrf52840/src/bin/usb_serial.rs b/examples/nrf52840/src/bin/usb_serial.rs new file mode 100644 index 000000000..7c9c4184b --- /dev/null +++ b/examples/nrf52840/src/bin/usb_serial.rs | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::mem; | ||
| 6 | |||
| 7 | use defmt::{info, panic}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_futures::join::join; | ||
| 10 | use embassy_nrf::usb::{Driver, Instance, PowerUsb, UsbSupply}; | ||
| 11 | use embassy_nrf::{interrupt, pac}; | ||
| 12 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 13 | use embassy_usb::driver::EndpointError; | ||
| 14 | use embassy_usb::{Builder, Config}; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | #[embassy_executor::main] | ||
| 18 | async fn main(_spawner: Spawner) { | ||
| 19 | let p = embassy_nrf::init(Default::default()); | ||
| 20 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 21 | |||
| 22 | info!("Enabling ext hfosc..."); | ||
| 23 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 24 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 25 | |||
| 26 | // Create the driver, from the HAL. | ||
| 27 | let irq = interrupt::take!(USBD); | ||
| 28 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 29 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 30 | |||
| 31 | // Create embassy-usb Config | ||
| 32 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 33 | config.manufacturer = Some("Embassy"); | ||
| 34 | config.product = Some("USB-serial example"); | ||
| 35 | config.serial_number = Some("12345678"); | ||
| 36 | config.max_power = 100; | ||
| 37 | config.max_packet_size_0 = 64; | ||
| 38 | |||
| 39 | // Required for windows compatiblity. | ||
| 40 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 41 | config.device_class = 0xEF; | ||
| 42 | config.device_sub_class = 0x02; | ||
| 43 | config.device_protocol = 0x01; | ||
| 44 | config.composite_with_iads = true; | ||
| 45 | |||
| 46 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 47 | // It needs some buffers for building the descriptors. | ||
| 48 | let mut device_descriptor = [0; 256]; | ||
| 49 | let mut config_descriptor = [0; 256]; | ||
| 50 | let mut bos_descriptor = [0; 256]; | ||
| 51 | let mut control_buf = [0; 64]; | ||
| 52 | |||
| 53 | let mut state = State::new(); | ||
| 54 | |||
| 55 | let mut builder = Builder::new( | ||
| 56 | driver, | ||
| 57 | config, | ||
| 58 | &mut device_descriptor, | ||
| 59 | &mut config_descriptor, | ||
| 60 | &mut bos_descriptor, | ||
| 61 | &mut control_buf, | ||
| 62 | None, | ||
| 63 | ); | ||
| 64 | |||
| 65 | // Create classes on the builder. | ||
| 66 | let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); | ||
| 67 | |||
| 68 | // Build the builder. | ||
| 69 | let mut usb = builder.build(); | ||
| 70 | |||
| 71 | // Run the USB device. | ||
| 72 | let usb_fut = usb.run(); | ||
| 73 | |||
| 74 | // Do stuff with the class! | ||
| 75 | let echo_fut = async { | ||
| 76 | loop { | ||
| 77 | class.wait_connection().await; | ||
| 78 | info!("Connected"); | ||
| 79 | let _ = echo(&mut class).await; | ||
| 80 | info!("Disconnected"); | ||
| 81 | } | ||
| 82 | }; | ||
| 83 | |||
| 84 | // Run everything concurrently. | ||
| 85 | // If we had made everything `'static` above instead, we could do this using separate tasks instead. | ||
| 86 | join(usb_fut, echo_fut).await; | ||
| 87 | } | ||
| 88 | |||
| 89 | struct Disconnected {} | ||
| 90 | |||
| 91 | impl From<EndpointError> for Disconnected { | ||
| 92 | fn from(val: EndpointError) -> Self { | ||
| 93 | match val { | ||
| 94 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 95 | EndpointError::Disabled => Disconnected {}, | ||
| 96 | } | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | async fn echo<'d, T: Instance + 'd, P: UsbSupply + 'd>( | ||
| 101 | class: &mut CdcAcmClass<'d, Driver<'d, T, P>>, | ||
| 102 | ) -> Result<(), Disconnected> { | ||
| 103 | let mut buf = [0; 64]; | ||
| 104 | loop { | ||
| 105 | let n = class.read_packet(&mut buf).await?; | ||
| 106 | let data = &buf[..n]; | ||
| 107 | info!("data: {:x}", data); | ||
| 108 | class.write_packet(data).await?; | ||
| 109 | } | ||
| 110 | } | ||
diff --git a/examples/nrf52840/src/bin/usb_serial_multitask.rs b/examples/nrf52840/src/bin/usb_serial_multitask.rs new file mode 100644 index 000000000..93efc2fe6 --- /dev/null +++ b/examples/nrf52840/src/bin/usb_serial_multitask.rs | |||
| @@ -0,0 +1,118 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use core::mem; | ||
| 6 | |||
| 7 | use defmt::{info, panic, unwrap}; | ||
| 8 | use embassy_executor::Spawner; | ||
| 9 | use embassy_nrf::usb::{Driver, PowerUsb}; | ||
| 10 | use embassy_nrf::{interrupt, pac, peripherals}; | ||
| 11 | use embassy_usb::class::cdc_acm::{CdcAcmClass, State}; | ||
| 12 | use embassy_usb::driver::EndpointError; | ||
| 13 | use embassy_usb::{Builder, Config, UsbDevice}; | ||
| 14 | use static_cell::StaticCell; | ||
| 15 | use {defmt_rtt as _, panic_probe as _}; | ||
| 16 | |||
| 17 | type MyDriver = Driver<'static, peripherals::USBD, PowerUsb>; | ||
| 18 | |||
| 19 | #[embassy_executor::task] | ||
| 20 | async fn usb_task(mut device: UsbDevice<'static, MyDriver>) { | ||
| 21 | device.run().await; | ||
| 22 | } | ||
| 23 | |||
| 24 | #[embassy_executor::task] | ||
| 25 | async fn echo_task(mut class: CdcAcmClass<'static, MyDriver>) { | ||
| 26 | loop { | ||
| 27 | class.wait_connection().await; | ||
| 28 | info!("Connected"); | ||
| 29 | let _ = echo(&mut class).await; | ||
| 30 | info!("Disconnected"); | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | #[embassy_executor::main] | ||
| 35 | async fn main(spawner: Spawner) { | ||
| 36 | let p = embassy_nrf::init(Default::default()); | ||
| 37 | let clock: pac::CLOCK = unsafe { mem::transmute(()) }; | ||
| 38 | |||
| 39 | info!("Enabling ext hfosc..."); | ||
| 40 | clock.tasks_hfclkstart.write(|w| unsafe { w.bits(1) }); | ||
| 41 | while clock.events_hfclkstarted.read().bits() != 1 {} | ||
| 42 | // Create the driver, from the HAL. | ||
| 43 | let irq = interrupt::take!(USBD); | ||
| 44 | let power_irq = interrupt::take!(POWER_CLOCK); | ||
| 45 | let driver = Driver::new(p.USBD, irq, PowerUsb::new(power_irq)); | ||
| 46 | |||
| 47 | // Create embassy-usb Config | ||
| 48 | let mut config = Config::new(0xc0de, 0xcafe); | ||
| 49 | config.manufacturer = Some("Embassy"); | ||
| 50 | config.product = Some("USB-serial example"); | ||
| 51 | config.serial_number = Some("12345678"); | ||
| 52 | config.max_power = 100; | ||
| 53 | config.max_packet_size_0 = 64; | ||
| 54 | |||
| 55 | // Required for windows compatiblity. | ||
| 56 | // https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help | ||
| 57 | config.device_class = 0xEF; | ||
| 58 | config.device_sub_class = 0x02; | ||
| 59 | config.device_protocol = 0x01; | ||
| 60 | config.composite_with_iads = true; | ||
| 61 | |||
| 62 | struct Resources { | ||
| 63 | device_descriptor: [u8; 256], | ||
| 64 | config_descriptor: [u8; 256], | ||
| 65 | bos_descriptor: [u8; 256], | ||
| 66 | control_buf: [u8; 64], | ||
| 67 | serial_state: State<'static>, | ||
| 68 | } | ||
| 69 | static RESOURCES: StaticCell<Resources> = StaticCell::new(); | ||
| 70 | let res = RESOURCES.init(Resources { | ||
| 71 | device_descriptor: [0; 256], | ||
| 72 | config_descriptor: [0; 256], | ||
| 73 | bos_descriptor: [0; 256], | ||
| 74 | control_buf: [0; 64], | ||
| 75 | serial_state: State::new(), | ||
| 76 | }); | ||
| 77 | |||
| 78 | // Create embassy-usb DeviceBuilder using the driver and config. | ||
| 79 | let mut builder = Builder::new( | ||
| 80 | driver, | ||
| 81 | config, | ||
| 82 | &mut res.device_descriptor, | ||
| 83 | &mut res.config_descriptor, | ||
| 84 | &mut res.bos_descriptor, | ||
| 85 | &mut res.control_buf, | ||
| 86 | None, | ||
| 87 | ); | ||
| 88 | |||
| 89 | // Create classes on the builder. | ||
| 90 | let class = CdcAcmClass::new(&mut builder, &mut res.serial_state, 64); | ||
| 91 | |||
| 92 | // Build the builder. | ||
| 93 | let usb = builder.build(); | ||
| 94 | |||
| 95 | unwrap!(spawner.spawn(usb_task(usb))); | ||
| 96 | unwrap!(spawner.spawn(echo_task(class))); | ||
| 97 | } | ||
| 98 | |||
| 99 | struct Disconnected {} | ||
| 100 | |||
| 101 | impl From<EndpointError> for Disconnected { | ||
| 102 | fn from(val: EndpointError) -> Self { | ||
| 103 | match val { | ||
| 104 | EndpointError::BufferOverflow => panic!("Buffer overflow"), | ||
| 105 | EndpointError::Disabled => Disconnected {}, | ||
| 106 | } | ||
| 107 | } | ||
| 108 | } | ||
| 109 | |||
| 110 | async fn echo(class: &mut CdcAcmClass<'static, MyDriver>) -> Result<(), Disconnected> { | ||
| 111 | let mut buf = [0; 64]; | ||
| 112 | loop { | ||
| 113 | let n = class.read_packet(&mut buf).await?; | ||
| 114 | let data = &buf[..n]; | ||
| 115 | info!("data: {:x}", data); | ||
| 116 | class.write_packet(data).await?; | ||
| 117 | } | ||
| 118 | } | ||
diff --git a/examples/nrf52840/src/bin/wdt.rs b/examples/nrf52840/src/bin/wdt.rs new file mode 100644 index 000000000..b0b9c3b81 --- /dev/null +++ b/examples/nrf52840/src/bin/wdt.rs | |||
| @@ -0,0 +1,41 @@ | |||
| 1 | #![no_std] | ||
| 2 | #![no_main] | ||
| 3 | #![feature(type_alias_impl_trait)] | ||
| 4 | |||
| 5 | use defmt::*; | ||
| 6 | use embassy_executor::Spawner; | ||
| 7 | use embassy_nrf::gpio::{Input, Pull}; | ||
| 8 | use embassy_nrf::wdt::{Config, Watchdog}; | ||
| 9 | use {defmt_rtt as _, panic_probe as _}; | ||
| 10 | |||
| 11 | #[embassy_executor::main] | ||
| 12 | async fn main(_spawner: Spawner) { | ||
| 13 | let p = embassy_nrf::init(Default::default()); | ||
| 14 | info!("Hello World!"); | ||
| 15 | |||
| 16 | let mut config = Config::default(); | ||
| 17 | config.timeout_ticks = 32768 * 3; // 3 seconds | ||
| 18 | |||
| 19 | // This is needed for `probe-run` to be able to catch the panic message | ||
| 20 | // in the WDT interrupt. The core resets 2 ticks after firing the interrupt. | ||
| 21 | config.run_during_debug_halt = false; | ||
| 22 | |||
| 23 | let (_wdt, [mut handle]) = match Watchdog::try_new(p.WDT, config) { | ||
| 24 | Ok(x) => x, | ||
| 25 | Err(_) => { | ||
| 26 | info!("Watchdog already active with wrong config, waiting for it to timeout..."); | ||
| 27 | loop {} | ||
| 28 | } | ||
| 29 | }; | ||
| 30 | |||
| 31 | let mut button = Input::new(p.P0_11, Pull::Up); | ||
| 32 | |||
| 33 | info!("Watchdog started, press button 1 to pet it or I'll reset in 3 seconds!"); | ||
| 34 | |||
| 35 | loop { | ||
| 36 | button.wait_for_high().await; | ||
| 37 | button.wait_for_low().await; | ||
| 38 | info!("Button pressed, petting watchdog!"); | ||
| 39 | handle.pet(); | ||
| 40 | } | ||
| 41 | } | ||
