summaryrefslogtreecommitdiff
path: root/example-rtic/src
diff options
context:
space:
mode:
authorDario Nieuwenhuis <dirbaio@dirbaio.net>2020-09-14 17:09:19 +0200
committerDario Nieuwenhuis <dirbaio@dirbaio.net>2020-09-14 17:09:19 +0200
commit35b20c6d7ee3fc07a7e7d7d5379c9dd55a994b40 (patch)
treee5ae661b8cfb7b2b72c57e07aab8fa9d2ed04a27 /example-rtic/src
parent9d3e5ac4c8393c78b50f1d4c63536762396f89eb (diff)
downloadnrf-softdevice-35b20c6d7ee3fc07a7e7d7d5379c9dd55a994b40.zip
Add RTIC example.
Diffstat (limited to 'example-rtic/src')
-rw-r--r--example-rtic/src/example_common.rs68
-rw-r--r--example-rtic/src/main.rs134
2 files changed, 202 insertions, 0 deletions
diff --git a/example-rtic/src/example_common.rs b/example-rtic/src/example_common.rs
new file mode 100644
index 0000000..e991915
--- /dev/null
+++ b/example-rtic/src/example_common.rs
@@ -0,0 +1,68 @@
+#![macro_use]
+
+use defmt_rtt as _; // global logger
+use nrf52840_hal as _;
+use panic_probe as _;
+use static_executor_cortex_m as _;
+
+pub use defmt::{info, intern};
+
+use core::sync::atomic::{AtomicUsize, Ordering};
+
+#[defmt::timestamp]
+fn timestamp() -> u64 {
+ static COUNT: AtomicUsize = AtomicUsize::new(0);
+ // NOTE(no-CAS) `timestamps` runs with interrupts disabled
+ let n = COUNT.load(Ordering::Relaxed);
+ COUNT.store(n + 1, Ordering::Relaxed);
+ n as u64
+}
+
+macro_rules! depanic {
+ ($( $i:expr ),*) => {
+ {
+ defmt::error!($( $i ),*);
+ panic!();
+ }
+ }
+}
+
+pub trait Dewrap<T> {
+ /// dewrap = defmt unwrap
+ fn dewrap(self) -> T;
+
+ /// dexpect = defmt expect
+ fn dexpect<M: defmt::Format>(self, msg: M) -> T;
+}
+
+impl<T> Dewrap<T> for Option<T> {
+ fn dewrap(self) -> T {
+ match self {
+ Some(t) => t,
+ None => depanic!("Dewrap failed: enum is none"),
+ }
+ }
+
+ fn dexpect<M: defmt::Format>(self, msg: M) -> T {
+ match self {
+ Some(t) => t,
+ None => depanic!("Unexpected None: {:?}", msg),
+ }
+ }
+}
+
+impl<T, E: defmt::Format> Dewrap<T> for Result<T, E> {
+ fn dewrap(self) -> T {
+ match self {
+ Ok(t) => t,
+ Err(e) => depanic!("Dewrap failed: {:?}", e),
+ }
+ }
+
+ fn dexpect<M: defmt::Format>(self, msg: M) -> T {
+ match self {
+ Ok(t) => t,
+ Err(e) => depanic!("Unexpected error: {:?}: {:?}", msg, e),
+ }
+ }
+}
diff --git a/example-rtic/src/main.rs b/example-rtic/src/main.rs
new file mode 100644
index 0000000..4eec5a1
--- /dev/null
+++ b/example-rtic/src/main.rs
@@ -0,0 +1,134 @@
+#![no_main]
+#![no_std]
+#![feature(type_alias_impl_trait)]
+
+mod example_common;
+use example_common::*;
+
+use core::mem;
+use nrf52840_hal::pac::TIMER1;
+use nrf52840_hal::prelude::*;
+use nrf52840_hal::timer::{Periodic, Timer};
+use nrf_softdevice::ble::peripheral;
+use nrf_softdevice::{raw, Softdevice};
+use rtic::app;
+
+// This example showcases how to use nrf-softdevice inside RTIC.
+//
+// It mixes RTIC's real-time interrupt-based multitasking with
+// static-executor's cooperative async/await multitasking.
+//
+// static-executor is run in RTIC's idle task, at lowest priority, so all RTIC
+// tasks will preempt async tasks if needed.
+//
+// Note that this is not fully safe: you must not use the softdevice's reserved
+// priorities for RTIC tasks. There is no compile-time checking for that for now.
+
+#[static_executor::task]
+async fn softdevice_task(sd: &'static Softdevice) {
+ sd.run().await;
+}
+
+#[static_executor::task]
+async fn bluetooth_task(sd: &'static Softdevice) {
+ #[rustfmt::skip]
+ let adv_data = &[
+ 0x02, 0x01, raw::BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE as u8,
+ 0x03, 0x03, 0x09, 0x18,
+ 0x0a, 0x09, b'H', b'e', b'l', b'l', b'o', b'R', b'T', b'I', b'C',
+ ];
+ #[rustfmt::skip]
+ let scan_data = &[
+ 0x03, 0x03, 0x09, 0x18,
+ ];
+
+ loop {
+ let _conn = peripheral::advertise(
+ sd,
+ peripheral::ConnectableAdvertisement::ScannableUndirected {
+ adv_data,
+ scan_data,
+ },
+ )
+ .await
+ .dewrap();
+
+ info!("advertising done!");
+
+ // conn will now get disconnected because it's dropped
+ }
+}
+
+#[app(device = nrf52840_hal::pac, peripherals = true)]
+const APP: () = {
+ struct Resources {
+ timer: Timer<TIMER1, Periodic>,
+ }
+
+ #[init()]
+ fn init(cx: init::Context) -> init::LateResources {
+ info!("init");
+
+ let mut timer = Timer::new(cx.device.TIMER1);
+ timer.enable_interrupt();
+ let mut timer = timer.into_periodic();
+ timer.start(1_000_000u32); // 1Mhz, so once per second
+
+ init::LateResources { timer }
+ }
+
+ #[idle]
+ fn idle(_: idle::Context) -> ! {
+ let config = nrf_softdevice::Config {
+ clock: Some(raw::nrf_clock_lf_cfg_t {
+ source: raw::NRF_CLOCK_LF_SRC_XTAL as u8,
+ rc_ctiv: 0,
+ rc_temp_ctiv: 0,
+ accuracy: 7,
+ }),
+ conn_gap: Some(raw::ble_gap_conn_cfg_t {
+ conn_count: 6,
+ event_length: 6,
+ }),
+ conn_gatt: Some(raw::ble_gatt_conn_cfg_t { att_mtu: 128 }),
+ gatts_attr_tab_size: Some(raw::ble_gatts_cfg_attr_tab_size_t {
+ attr_tab_size: 32768,
+ }),
+ gap_role_count: Some(raw::ble_gap_cfg_role_count_t {
+ adv_set_count: 1,
+ periph_role_count: 3,
+ central_role_count: 3,
+ central_sec_count: 0,
+ _bitfield_1: raw::ble_gap_cfg_role_count_t::new_bitfield_1(0),
+ }),
+ gap_device_name: Some(raw::ble_gap_cfg_device_name_t {
+ p_value: b"HelloRTIC" as *const u8 as _,
+ current_len: 9,
+ max_len: 9,
+ write_perm: unsafe { mem::zeroed() },
+ _bitfield_1: raw::ble_gap_cfg_device_name_t::new_bitfield_1(
+ raw::BLE_GATTS_VLOC_STACK as u8,
+ ),
+ }),
+ ..Default::default()
+ };
+
+ // Softdevice enable must not be done in RTIC init
+ // because RTIC runs init with interrupts disabled, and the
+ // softdevice crashes if it's enabled with interrupts disabled.
+ let sd = Softdevice::enable(&config);
+
+ unsafe {
+ softdevice_task.spawn(sd).dewrap();
+ bluetooth_task.spawn(sd).dewrap();
+
+ static_executor::run();
+ }
+ }
+
+ #[task(binds = TIMER1, resources = [timer], priority = 1)]
+ fn exec(cx: exec::Context) {
+ cx.resources.timer.wait().unwrap();
+ info!("tick");
+ }
+};