From f8afc3c8828c334baaa399f73764ba45a3f69799 Mon Sep 17 00:00:00 2001 From: Dominik Boehi Date: Sun, 8 Jan 2023 15:36:35 +0100 Subject: [PATCH] Add samples for nrf5340 --- examples/nrf5340/.cargo/config.toml | 9 +++ examples/nrf5340/Cargo.toml | 64 ++++++++++++++++++ examples/nrf5340/build.rs | 35 ++++++++++ examples/nrf5340/memory.x | 7 ++ examples/nrf5340/src/bin/awaitable_timer.rs | 26 ++++++++ examples/nrf5340/src/bin/blinky.rs | 21 ++++++ examples/nrf5340/src/bin/buffered_uart.rs | 57 ++++++++++++++++ examples/nrf5340/src/bin/channel.rs | 43 ++++++++++++ .../src/bin/channel_sender_receiver.rs | 50 ++++++++++++++ examples/nrf5340/src/bin/gpiote_channel.rs | 66 +++++++++++++++++++ examples/nrf5340/src/bin/gpiote_port.rs | 34 ++++++++++ examples/nrf5340/src/bin/uart.rs | 35 ++++++++++ examples/nrf5340/src/bin/uart_idle.rs | 35 ++++++++++ examples/nrf5340/src/bin/uart_split.rs | 60 +++++++++++++++++ 14 files changed, 542 insertions(+) create mode 100644 examples/nrf5340/.cargo/config.toml create mode 100644 examples/nrf5340/Cargo.toml create mode 100644 examples/nrf5340/build.rs create mode 100644 examples/nrf5340/memory.x create mode 100644 examples/nrf5340/src/bin/awaitable_timer.rs create mode 100644 examples/nrf5340/src/bin/blinky.rs create mode 100644 examples/nrf5340/src/bin/buffered_uart.rs create mode 100644 examples/nrf5340/src/bin/channel.rs create mode 100644 examples/nrf5340/src/bin/channel_sender_receiver.rs create mode 100644 examples/nrf5340/src/bin/gpiote_channel.rs create mode 100644 examples/nrf5340/src/bin/gpiote_port.rs create mode 100644 examples/nrf5340/src/bin/uart.rs create mode 100644 examples/nrf5340/src/bin/uart_idle.rs create mode 100644 examples/nrf5340/src/bin/uart_split.rs diff --git a/examples/nrf5340/.cargo/config.toml b/examples/nrf5340/.cargo/config.toml new file mode 100644 index 000000000..ff0879c8c --- /dev/null +++ b/examples/nrf5340/.cargo/config.toml @@ -0,0 +1,9 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# replace nRF5340_xxAA with your chip as listed in `probe-run --list-chips` +runner = "probe-run --chip nRF5340_xxAA" + +[build] +target = "thumbv8m.main-none-eabihf" + +[env] +DEFMT_LOG = "trace" diff --git a/examples/nrf5340/Cargo.toml b/examples/nrf5340/Cargo.toml new file mode 100644 index 000000000..03485711e --- /dev/null +++ b/examples/nrf5340/Cargo.toml @@ -0,0 +1,64 @@ +[package] +edition = "2021" +name = "embassy-nrf-examples" +version = "0.1.0" +license = "MIT OR Apache-2.0" + +[features] +default = ["nightly"] +nightly = [ + "embassy-executor/nightly", + "embassy-nrf/nightly", + "embassy-net/nightly", + "embassy-nrf/unstable-traits", + "embassy-usb", + "embedded-io/async", + "embassy-net", +] + +[dependencies] +embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } +embassy-sync = { version = "0.1.0", path = "../../embassy-sync", features = [ + "defmt", +] } +embassy-executor = { version = "0.1.0", path = "../../embassy-executor", features = [ + "defmt", + "integrated-timers", +] } +embassy-time = { version = "0.1.0", path = "../../embassy-time", features = [ + "defmt", + "defmt-timestamp-uptime", +] } +embassy-nrf = { version = "0.1.0", path = "../../embassy-nrf", features = [ + "defmt", + "nrf5340-app-s", + "time-driver-rtc1", + "gpiote", + "unstable-pac", +] } +embassy-net = { version = "0.1.0", path = "../../embassy-net", features = [ + "defmt", + "tcp", + "dhcpv4", + "medium-ethernet", +], optional = true } +embassy-usb = { version = "0.1.0", path = "../../embassy-usb", features = [ + "defmt", +], optional = true } +embedded-io = "0.4.0" + + +defmt = "0.3" +defmt-rtt = "0.4" + +static_cell = "1.0" +cortex-m = { version = "0.7.6", features = ["critical-section-single-core"] } +cortex-m-rt = "0.7.0" +panic-probe = { version = "0.3", features = ["print-defmt"] } +futures = { version = "0.3.17", default-features = false, features = [ + "async-await", +] } +rand = { version = "0.8.4", default-features = false } +embedded-storage = "0.3.0" +usbd-hid = "0.6.0" +serde = { version = "1.0.136", default-features = false } diff --git a/examples/nrf5340/build.rs b/examples/nrf5340/build.rs new file mode 100644 index 000000000..30691aa97 --- /dev/null +++ b/examples/nrf5340/build.rs @@ -0,0 +1,35 @@ +//! This build script copies the `memory.x` file from the crate root into +//! a directory where the linker can always find it at build time. +//! For many projects this is optional, as the linker always searches the +//! project root directory -- wherever `Cargo.toml` is. However, if you +//! are using a workspace or have a more complicated build setup, this +//! build script becomes required. Additionally, by requesting that +//! Cargo re-run the build script whenever `memory.x` is changed, +//! updating `memory.x` ensures a rebuild of the application with the +//! new memory settings. + +use std::env; +use std::fs::File; +use std::io::Write; +use std::path::PathBuf; + +fn main() { + // Put `memory.x` in our output directory and ensure it's + // on the linker search path. + let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap()); + File::create(out.join("memory.x")) + .unwrap() + .write_all(include_bytes!("memory.x")) + .unwrap(); + println!("cargo:rustc-link-search={}", out.display()); + + // By default, Cargo will re-run a build script whenever + // any file in the project changes. By specifying `memory.x` + // here, we ensure the build script is only re-run when + // `memory.x` is changed. + println!("cargo:rerun-if-changed=memory.x"); + + println!("cargo:rustc-link-arg-bins=--nmagic"); + println!("cargo:rustc-link-arg-bins=-Tlink.x"); + println!("cargo:rustc-link-arg-bins=-Tdefmt.x"); +} diff --git a/examples/nrf5340/memory.x b/examples/nrf5340/memory.x new file mode 100644 index 000000000..a122dc24a --- /dev/null +++ b/examples/nrf5340/memory.x @@ -0,0 +1,7 @@ +MEMORY +{ + /* NOTE 1 K = 1 KiBi = 1024 bytes */ + /* These values correspond to the NRF5340 */ + FLASH : ORIGIN = 0x00000000, LENGTH = 1024K + RAM : ORIGIN = 0x20000000, LENGTH = 256K +} diff --git a/examples/nrf5340/src/bin/awaitable_timer.rs b/examples/nrf5340/src/bin/awaitable_timer.rs new file mode 100644 index 000000000..b32af236c --- /dev/null +++ b/examples/nrf5340/src/bin/awaitable_timer.rs @@ -0,0 +1,26 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::interrupt; +use embassy_nrf::timer::Timer; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut t = Timer::new_awaitable(p.TIMER0, interrupt::take!(TIMER0)); + // default frequency is 1MHz, so this triggers every second + t.cc(0).write(1_000_000); + // clear the timer value on cc[0] compare match + t.cc(0).short_compare_clear(); + t.start(); + + loop { + // wait for compare match + t.cc(0).wait().await; + info!("hardware timer tick"); + } +} diff --git a/examples/nrf5340/src/bin/blinky.rs b/examples/nrf5340/src/bin/blinky.rs new file mode 100644 index 000000000..3422cedf0 --- /dev/null +++ b/examples/nrf5340/src/bin/blinky.rs @@ -0,0 +1,21 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P0_28, Level::Low, OutputDrive::Standard); + + loop { + led.set_high(); + Timer::after(Duration::from_millis(300)).await; + led.set_low(); + Timer::after(Duration::from_millis(300)).await; + } +} diff --git a/examples/nrf5340/src/bin/buffered_uart.rs b/examples/nrf5340/src/bin/buffered_uart.rs new file mode 100644 index 000000000..25a0ca237 --- /dev/null +++ b/examples/nrf5340/src/bin/buffered_uart.rs @@ -0,0 +1,57 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::buffered_uarte::{BufferedUarte, State}; +use embassy_nrf::{interrupt, uarte}; +use embedded_io::asynch::{BufRead, Write}; +use futures::pin_mut; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let mut tx_buffer = [0u8; 4096]; + let mut rx_buffer = [0u8; 4096]; + + let irq = interrupt::take!(SERIAL0); + let mut state = State::new(); + // Please note - important to have hardware flow control (https://github.com/embassy-rs/embassy/issues/536) + let u = BufferedUarte::new( + &mut state, + p.UARTETWISPI0, + p.TIMER0, + p.PPI_CH0, + p.PPI_CH1, + irq, + p.P0_08, + p.P0_06, + p.P0_07, + p.P0_05, + config, + &mut rx_buffer, + &mut tx_buffer, + ); + pin_mut!(u); + + info!("uarte initialized!"); + + unwrap!(u.write_all(b"Hello!\r\n").await); + info!("wrote hello in uart!"); + + loop { + info!("reading..."); + let buf = unwrap!(u.fill_buf().await); + info!("read done, got {}", buf); + + // Read bytes have to be explicitly consumed, otherwise fill_buf() will return them again + let n = buf.len(); + u.consume(n); + } +} diff --git a/examples/nrf5340/src/bin/channel.rs b/examples/nrf5340/src/bin/channel.rs new file mode 100644 index 000000000..425d43051 --- /dev/null +++ b/examples/nrf5340/src/bin/channel.rs @@ -0,0 +1,43 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::unwrap; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Level, Output, OutputDrive}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; +use embassy_time::{Duration, Timer}; +use {defmt_rtt as _, panic_probe as _}; + +enum LedState { + On, + Off, +} + +static CHANNEL: Channel = Channel::new(); + +#[embassy_executor::task] +async fn my_task() { + loop { + CHANNEL.send(LedState::On).await; + Timer::after(Duration::from_secs(1)).await; + CHANNEL.send(LedState::Off).await; + Timer::after(Duration::from_secs(1)).await; + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut led = Output::new(p.P0_28, Level::Low, OutputDrive::Standard); + + unwrap!(spawner.spawn(my_task())); + + loop { + match CHANNEL.recv().await { + LedState::On => led.set_high(), + LedState::Off => led.set_low(), + } + } +} diff --git a/examples/nrf5340/src/bin/channel_sender_receiver.rs b/examples/nrf5340/src/bin/channel_sender_receiver.rs new file mode 100644 index 000000000..9628c0525 --- /dev/null +++ b/examples/nrf5340/src/bin/channel_sender_receiver.rs @@ -0,0 +1,50 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::unwrap; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{AnyPin, Level, Output, OutputDrive, Pin}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::channel::{Channel, Receiver, Sender}; +use embassy_time::{Duration, Timer}; +use static_cell::StaticCell; +use {defmt_rtt as _, panic_probe as _}; + +enum LedState { + On, + Off, +} + +static CHANNEL: StaticCell> = StaticCell::new(); + +#[embassy_executor::task] +async fn send_task(sender: Sender<'static, NoopRawMutex, LedState, 1>) { + loop { + sender.send(LedState::On).await; + Timer::after(Duration::from_secs(1)).await; + sender.send(LedState::Off).await; + Timer::after(Duration::from_secs(1)).await; + } +} + +#[embassy_executor::task] +async fn recv_task(led: AnyPin, receiver: Receiver<'static, NoopRawMutex, LedState, 1>) { + let mut led = Output::new(led, Level::Low, OutputDrive::Standard); + + loop { + match receiver.recv().await { + LedState::On => led.set_high(), + LedState::Off => led.set_low(), + } + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let channel = CHANNEL.init(Channel::new()); + + unwrap!(spawner.spawn(send_task(channel.sender()))); + unwrap!(spawner.spawn(recv_task(p.P0_28.degrade(), channel.receiver()))); +} diff --git a/examples/nrf5340/src/bin/gpiote_channel.rs b/examples/nrf5340/src/bin/gpiote_channel.rs new file mode 100644 index 000000000..ceab1194a --- /dev/null +++ b/examples/nrf5340/src/bin/gpiote_channel.rs @@ -0,0 +1,66 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::info; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{Input, Pull}; +use embassy_nrf::gpiote::{InputChannel, InputChannelPolarity}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("Starting!"); + + let ch1 = InputChannel::new( + p.GPIOTE_CH0, + Input::new(p.P0_23, Pull::Up), + InputChannelPolarity::HiToLo, + ); + let ch2 = InputChannel::new( + p.GPIOTE_CH1, + Input::new(p.P0_24, Pull::Up), + InputChannelPolarity::LoToHi, + ); + let ch3 = InputChannel::new( + p.GPIOTE_CH2, + Input::new(p.P0_08, Pull::Up), + InputChannelPolarity::Toggle, + ); + let ch4 = InputChannel::new( + p.GPIOTE_CH3, + Input::new(p.P0_09, Pull::Up), + InputChannelPolarity::Toggle, + ); + + let button1 = async { + loop { + ch1.wait().await; + info!("Button 1 pressed") + } + }; + + let button2 = async { + loop { + ch2.wait().await; + info!("Button 2 released") + } + }; + + let button3 = async { + loop { + ch3.wait().await; + info!("Button 3 toggled") + } + }; + + let button4 = async { + loop { + ch4.wait().await; + info!("Button 4 toggled") + } + }; + + futures::join!(button1, button2, button3, button4); +} diff --git a/examples/nrf5340/src/bin/gpiote_port.rs b/examples/nrf5340/src/bin/gpiote_port.rs new file mode 100644 index 000000000..0cc911ad2 --- /dev/null +++ b/examples/nrf5340/src/bin/gpiote_port.rs @@ -0,0 +1,34 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::{info, unwrap}; +use embassy_executor::Spawner; +use embassy_nrf::gpio::{AnyPin, Input, Pin as _, Pull}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::task(pool_size = 4)] +async fn button_task(n: usize, mut pin: Input<'static, AnyPin>) { + loop { + pin.wait_for_low().await; + info!("Button {:?} pressed!", n); + pin.wait_for_high().await; + info!("Button {:?} released!", n); + } +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + info!("Starting!"); + + let btn1 = Input::new(p.P0_23.degrade(), Pull::Up); + let btn2 = Input::new(p.P0_24.degrade(), Pull::Up); + let btn3 = Input::new(p.P0_08.degrade(), Pull::Up); + let btn4 = Input::new(p.P0_09.degrade(), Pull::Up); + + unwrap!(spawner.spawn(button_task(1, btn1))); + unwrap!(spawner.spawn(button_task(2, btn2))); + unwrap!(spawner.spawn(button_task(3, btn3))); + unwrap!(spawner.spawn(button_task(4, btn4))); +} diff --git a/examples/nrf5340/src/bin/uart.rs b/examples/nrf5340/src/bin/uart.rs new file mode 100644 index 000000000..1db450576 --- /dev/null +++ b/examples/nrf5340/src/bin/uart.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::{interrupt, uarte}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let irq = interrupt::take!(SERIAL0); + let mut uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + + info!("uarte initialized!"); + + // Message must be in SRAM + let mut buf = [0; 8]; + buf.copy_from_slice(b"Hello!\r\n"); + + unwrap!(uart.write(&buf).await); + info!("wrote hello in uart!"); + + loop { + info!("reading..."); + unwrap!(uart.read(&mut buf).await); + info!("writing..."); + unwrap!(uart.write(&buf).await); + } +} diff --git a/examples/nrf5340/src/bin/uart_idle.rs b/examples/nrf5340/src/bin/uart_idle.rs new file mode 100644 index 000000000..327fc4b23 --- /dev/null +++ b/examples/nrf5340/src/bin/uart_idle.rs @@ -0,0 +1,35 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::{interrupt, uarte}; +use {defmt_rtt as _, panic_probe as _}; + +#[embassy_executor::main] +async fn main(_spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let irq = interrupt::take!(SERIAL0); + let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + let (mut tx, mut rx) = uart.split_with_idle(p.TIMER0, p.PPI_CH0, p.PPI_CH1); + + info!("uarte initialized!"); + + // Message must be in SRAM + let mut buf = [0; 8]; + buf.copy_from_slice(b"Hello!\r\n"); + + unwrap!(tx.write(&buf).await); + info!("wrote hello in uart!"); + + loop { + info!("reading..."); + let n = unwrap!(rx.read_until_idle(&mut buf).await); + info!("got {} bytes", n); + } +} diff --git a/examples/nrf5340/src/bin/uart_split.rs b/examples/nrf5340/src/bin/uart_split.rs new file mode 100644 index 000000000..1bff382fb --- /dev/null +++ b/examples/nrf5340/src/bin/uart_split.rs @@ -0,0 +1,60 @@ +#![no_std] +#![no_main] +#![feature(type_alias_impl_trait)] + +use defmt::*; +use embassy_executor::Spawner; +use embassy_nrf::peripherals::UARTETWISPI0; +use embassy_nrf::uarte::UarteRx; +use embassy_nrf::{interrupt, uarte}; +use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex; +use embassy_sync::channel::Channel; +use {defmt_rtt as _, panic_probe as _}; + +static CHANNEL: Channel = Channel::new(); + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_nrf::init(Default::default()); + let mut config = uarte::Config::default(); + config.parity = uarte::Parity::EXCLUDED; + config.baudrate = uarte::Baudrate::BAUD115200; + + let irq = interrupt::take!(SERIAL0); + let uart = uarte::Uarte::new(p.UARTETWISPI0, irq, p.P0_08, p.P0_06, config); + let (mut tx, rx) = uart.split(); + + info!("uarte initialized!"); + + // Spawn a task responsible purely for reading + + unwrap!(spawner.spawn(reader(rx))); + + // Message must be in SRAM + { + let mut buf = [0; 23]; + buf.copy_from_slice(b"Type 8 chars to echo!\r\n"); + + unwrap!(tx.write(&buf).await); + info!("wrote hello in uart!"); + } + + // Continue reading in this main task and write + // back out the buffer we receive from the read + // task. + loop { + let buf = CHANNEL.recv().await; + info!("writing..."); + unwrap!(tx.write(&buf).await); + } +} + +#[embassy_executor::task] +async fn reader(mut rx: UarteRx<'static, UARTETWISPI0>) { + let mut buf = [0; 8]; + loop { + info!("reading..."); + unwrap!(rx.read(&mut buf).await); + CHANNEL.send(buf).await; + } +}