diff options
| author | bors[bot] <26634292+bors[bot]@users.noreply.github.com> | 2023-01-10 11:06:22 +0000 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-01-10 11:06:22 +0000 |
| commit | dbf749370897290e16ef6ed2917d075c40358991 (patch) | |
| tree | 0ad516d336522f531514ac300e9c0adac1371250 /examples/nrf52840/src/bin | |
| parent | 3fbedd7c0953cab5f14433da6b9b1e2ff5d76939 (diff) | |
| parent | 2baebabf4dd2abecfd08ca078ecf59060d5ad585 (diff) | |
Merge #1149
1149: Add samples for nrf5340 r=lulf a=Tiwalun
Samples for the nrf5340, copied from the existing nrf samples.
Not sure if copying them is the best way of adding support, or using features in the existing samples would be better?
The code is mostly the same, with some different peripherals and pin mappings for the DK.
Co-authored-by: Dominik Boehi <[email protected]>
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 | } | ||
