summaryrefslogtreecommitdiffstats
path: root/ble
diff options
context:
space:
mode:
Diffstat (limited to 'ble')
-rw-r--r--ble/.gdbinit6
-rw-r--r--ble/Cargo.lock35
-rw-r--r--ble/Cargo.toml16
-rw-r--r--ble/src/logger.rs127
-rw-r--r--ble/src/macros.rs15
-rw-r--r--ble/src/main.rs70
-rw-r--r--ble/src/rtc.rs68
7 files changed, 316 insertions, 21 deletions
diff --git a/ble/.gdbinit b/ble/.gdbinit
index a32f910..c276c1a 100644
--- a/ble/.gdbinit
+++ b/ble/.gdbinit
@@ -7,7 +7,5 @@ set print asm-demangle on
#target extended-remote :3333
target remote :3333
#monitor flash breakpoints 1
-#monitor semihosting enable
-#monitor semihosting IOClient 3
-monitor reset
-load
+monitor arm semihosting enable
+#monitor arm semihosting IOClient 3
diff --git a/ble/Cargo.lock b/ble/Cargo.lock
index 42cc690..f706586 100644
--- a/ble/Cargo.lock
+++ b/ble/Cargo.lock
@@ -39,14 +39,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
name = "ble"
version = "0.1.0"
dependencies = [
+ "clint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-m-rt 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
+ "cortex-m-semihosting 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"nrf52840-hal 0.8.1",
"nrf52840-mdk-bsp 0.1.0",
"panic-semihosting 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rubble 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"rubble-nrf52 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
+ "starb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -60,6 +65,19 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "cfg-if"
+version = "0.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
+name = "clint"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "cortex-m"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -124,6 +142,14 @@ dependencies = [
]
[[package]]
+name = "log"
+version = "0.4.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+dependencies = [
+ "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
name = "nb"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -271,6 +297,11 @@ version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
+name = "starb"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+
+[[package]]
name = "syn"
version = "0.15.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -321,6 +352,8 @@ dependencies = [
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum cast 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427"
+"checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33"
+"checksum clint 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "200296af81a4cbe7da52afd9e5c35b7782eca865678caf2280251b777c9c37aa"
"checksum cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3c18719fdc57db65668bfc977db9a0fa1a41d718c5d9cd4f652c9d4b0e0956a"
"checksum cortex-m-rt 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "17805910e3ecf029bdbfcc42b7384d9e3d9e5626153fa810002c1ef9839338ac"
"checksum cortex-m-rt-macros 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d7ae692573e0acccb1579fef1abf5a5bf1d2f3f0149a22b16870ec9309aee25f"
@@ -328,6 +361,7 @@ dependencies = [
"checksum embedded-hal 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b"
"checksum fpa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f074479d683e5a8fd0bf1251d0a5d91b0d9178b867b44962191ed0eaaf8d4009"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
+"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum nb 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b1411551beb3c11dedfb0a90a0fa256b47d28b9ec2cdff34c25a2fa59e45dbdc"
"checksum nrf52840-pac 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e375b19eec5ae6c75387ea2fff3d5a6f989d65a0af350fcfe854f31243bfdfe2"
"checksum panic-semihosting 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "97cfb37c1d3b5f0cc18bf14485018cccd13bdd24f7b5bfd456c1d8760afef824"
@@ -343,6 +377,7 @@ dependencies = [
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
+"checksum starb 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "36de9f301031f2a692afe144c8a757950d2b60b7fbf447055b8e19a9c232fb2c"
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
diff --git a/ble/Cargo.toml b/ble/Cargo.toml
index ad7b888..ad11b18 100644
--- a/ble/Cargo.toml
+++ b/ble/Cargo.toml
@@ -11,8 +11,13 @@ rubble = "0.0"
rubble-nrf52 = "0.0"
cortex-m = "~0.6"
cortex-m-rt = "~0.6"
+embedded-hal = "0.2"
panic-semihosting = "~0.5"
nb = "~0.1"
+log = "0.4"
+starb = "0.1"
+clint = "0.2"
+cortex-m-semihosting = "0.3"
[features]
default = ["rt", "rubble-nrf52/52840"]
@@ -20,3 +25,14 @@ rt = ["nrf52840-hal/rt"]
[patch.crates-io]
nrf52840-hal = { path = "../../nrf52-hal/nrf52840-hal" }
+
+[profile.dev]
+incremental = false
+codegen-units = 1
+debug = true
+lto = false
+
+[profile.release]
+debug = true
+lto = true
+opt-level = "s"
diff --git a/ble/src/logger.rs b/ble/src/logger.rs
new file mode 100644
index 0000000..f102b74
--- /dev/null
+++ b/ble/src/logger.rs
@@ -0,0 +1,127 @@
+use crate::rtc;
+
+use core::cell::UnsafeCell;
+use core::fmt::{self, Write};
+use log::{Metadata, Record};
+use nrf52840_hal::{target::UARTE0, uarte, Uarte};
+use starb::{Reader, RingBuffer, Writer};
+
+static mut UARTE0: usize = 0;
+
+struct JoinedRingBuffer<'a> {
+ lbr: Reader<'a, u8>,
+ lbw: Writer<'a, u8>,
+}
+
+impl<'a> JoinedRingBuffer<'a> {
+ const fn new(rb: &'a RingBuffer<u8>) -> Self {
+ let (lbr, lbw) = rb.split();
+ Self { lbr: lbr, lbw: lbw }
+ }
+}
+
+impl fmt::Write for JoinedRingBuffer<'_> {
+ fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
+ for b in s.bytes() {
+ if let Err(_) = self.lbw.unshift(b) {
+ // Ignore buffer full errors for logging.
+ return Ok(());
+ }
+ }
+ Ok(())
+ }
+}
+
+static mut LB: RingBuffer<u8> = RingBuffer::<u8>::new(0);
+static mut JRB: JoinedRingBuffer = unsafe { JoinedRingBuffer::new(&LB) };
+
+// The UART isn't necessarily Sync, so wrap it in something that
+// is. As long as flush() is only called from one thread, we're fine,
+// but this is a guarantee that the logger module doesn't make.
+pub struct WriteWrapper<W> {
+ w: W,
+}
+impl<W> WriteWrapper<W> {
+ pub fn new(writer: W) -> Self {
+ Self { w: writer }
+ }
+}
+unsafe impl<W> Sync for WriteWrapper<W> {}
+
+pub struct SerialLogger<W> {
+ writer: UnsafeCell<WriteWrapper<Uarte<W>>>,
+}
+
+impl<W> SerialLogger<W>
+where
+ W: uarte::Instance,
+{
+ pub fn new(writer: WriteWrapper<Uarte<W>>) -> Self {
+ // Stash this for unsafe usage in case there's an issue with
+ // the rest of the logging.
+ unsafe { UARTE0 = core::mem::transmute(&writer.w) };
+ Self {
+ writer: UnsafeCell::new(writer),
+ }
+ }
+}
+unsafe impl<W> Send for SerialLogger<W> {}
+unsafe impl<W> Sync for SerialLogger<W> {}
+
+impl<W> log::Log for SerialLogger<W>
+where
+ W: uarte::Instance,
+{
+ fn enabled(&self, metadata: &Metadata) -> bool {
+ metadata.level() <= log::max_level()
+ }
+
+ fn log(&self, record: &Record) {
+ if !self.enabled(record.metadata()) {
+ return;
+ }
+
+ let jrb = unsafe { &mut JRB };
+ write!(
+ jrb,
+ "[{}] {} {} -- {}\r\n",
+ rtc::millis(),
+ record.level(),
+ record.target(),
+ record.args()
+ )
+ .ok();
+ }
+
+ fn flush(&self) {
+ // Unsafe due to mutable static. We can only deal with the
+ // tail position of the buffer here to keep things safe.
+ let jrb = unsafe { &mut JRB };
+ if jrb.lbr.is_empty() {
+ return;
+ }
+
+ let writer = unsafe { &mut (*self.writer.get()) };
+ while let Some(b) = jrb.lbr.shift() {
+ // TODO: The UARTE peripheral uses DMA to send a bunch of
+ // stuff at speed, in hardware. It would be nice to use
+ // it, but we can't just take a slice of the ring buffer,
+ // since we don't know about its internal structure here.
+ writer.w.write(&[b]).expect("writing log");
+ }
+ }
+}
+
+// Write to the UART right now, instead of putting it on a ring
+// buffer. This function is a huge hack, and only useful for debugging
+// either before the main loop starts or if the ring buffer is broken.
+pub unsafe fn write_fmt_now(args: fmt::Arguments, nl: bool) {
+ if UARTE0 == 0 {
+ return;
+ }
+ let uart: &mut Uarte<UARTE0> = core::mem::transmute(UARTE0);
+ fmt::write(uart, args).expect("writing fmt now to uart");
+ if nl {
+ uart.write_str("\r\n").expect("writing nl now to uart");
+ }
+}
diff --git a/ble/src/macros.rs b/ble/src/macros.rs
new file mode 100644
index 0000000..46d2d07
--- /dev/null
+++ b/ble/src/macros.rs
@@ -0,0 +1,15 @@
+#[macro_export]
+macro_rules! logln_now {
+ ($($arg:tt)*) => {
+ unsafe {crate::logger::write_fmt_now(format_args!($($arg)*), true);}
+ };
+ (_) => {};
+}
+
+#[macro_export]
+macro_rules! log_now {
+ ($($arg:tt)*) => {
+ unsafe {crate::logger::write_fmt_now(format_args!($($arg)*), false);}
+ };
+ (_) => {};
+}
diff --git a/ble/src/main.rs b/ble/src/main.rs
index 8b8c513..6f167a5 100644
--- a/ble/src/main.rs
+++ b/ble/src/main.rs
@@ -1,39 +1,75 @@
//! BLE interface.
+
#![no_std]
#![no_main]
-use core::fmt::Write;
+mod logger;
+mod macros;
+mod rtc;
+
+use clint::HandlerArray;
+use core::mem;
+use cortex_m::asm::wfi;
use cortex_m_rt::entry;
-use nb::block;
+use log::{info, trace, LevelFilter};
#[allow(unused_imports)]
extern crate panic_semihosting;
use nrf52840_mdk_bsp::{
hal::{
- prelude::*,
- timer::{self, Timer},
+ target::{interrupt, Interrupt, UARTE0},
+ Clocks, Rtc,
},
Board,
};
+// TODO:
+// * set up serial reader for trinket
+// * set up i²c interface for keyboard reports
+// * and, finally, bluetooth
+
+// Interrupt handler table.
+static HANDLERS: HandlerArray = HandlerArray::new();
+
#[entry]
fn main() -> ! {
- let mut nrf52 = Board::take().unwrap();
+ let nrf52 = Board::take().unwrap();
+
+ let uart_wrapped = logger::WriteWrapper::new(nrf52.cdc);
+ let logger = logger::SerialLogger::new(uart_wrapped);
+ let logger_ref: &'static logger::SerialLogger<UARTE0> = unsafe { mem::transmute(&logger) };
+ log::set_logger(logger_ref).expect("setting logger");
+ log::set_max_level(LevelFilter::Trace);
+
+ nrf52.RTC0.intenset.write(|w| w.tick().set());
+
+ let mut nvic = nrf52.NVIC;
+ let mut rtc_handler = rtc::setup(Rtc::new(nrf52.RTC0), Clocks::new(nrf52.CLOCK));
- let mut timer = Timer::new(nrf52.TIMER0);
+ logln_now!("logln_now?");
+ HANDLERS.with_overrides(|hs| {
+ hs.register(0, &mut rtc_handler);
+ nvic.enable(Interrupt::RTC0);
- write!(nrf52.cdc, "Bootstrap complete.").ok();
- loop {
- write!(nrf52.cdc, ".").ok();
- delay(&mut timer, 1_000_000); // 1s
- }
+ info!("Bootstrap complete.");
+ let mut last_tick = rtc::millis();
+ last_tick -= last_tick % 1024;
+ loop {
+ let tick = rtc::millis();
+ if tick >= last_tick + 1024 {
+ last_tick = tick;
+ last_tick -= last_tick % 1024;
+ trace!(".");
+ }
+ wfi();
+ }
+ });
+ unreachable!();
}
-fn delay<T>(timer: &mut Timer<T>, cycles: u32)
-where
- T: timer::Instance,
-{
- timer.start(cycles);
- let _ = block!(timer.wait());
+// TODO: RTC0/1/2 exist. See which is best, if it matters.
+#[interrupt]
+fn RTC0() {
+ HANDLERS.call(0);
}
diff --git a/ble/src/rtc.rs b/ble/src/rtc.rs
new file mode 100644
index 0000000..8fe581f
--- /dev/null
+++ b/ble/src/rtc.rs
@@ -0,0 +1,68 @@
+use core::sync::atomic::{AtomicUsize, Ordering};
+use log::trace;
+use nrf52840_hal::{
+ clocks::LfOscStopped,
+ rtc::{self, RtcInterrupt},
+ Clocks, Rtc,
+};
+
+struct MilliClock(AtomicUsize);
+impl MilliClock {
+ const fn new() -> Self {
+ Self(AtomicUsize::new(0))
+ }
+
+ #[allow(unused)]
+ fn set(&self, millis: usize) {
+ self.0.store(millis, Ordering::SeqCst)
+ }
+
+ #[allow(unused)]
+ fn add(&self, val: usize) -> usize {
+ self.0.fetch_add(val, Ordering::SeqCst)
+ }
+
+ // Slightly less than 1ms, due to using a 32,768Hz clock, we can't
+ // hit exactly 1ms, so we shoot for a bit under.
+ fn millis(&self) -> usize {
+ self.0.load(Ordering::SeqCst)
+ }
+}
+
+static CLOCK: MilliClock = MilliClock::new();
+
+pub fn setup<R, H, L>(
+ mut rtc: Rtc<R, rtc::Stopped>,
+ clock: Clocks<H, L, LfOscStopped>,
+) -> impl FnMut()
+where
+ R: rtc::Instance,
+{
+ trace!("setting up clock");
+
+ clock.set_lfclk_src_rc().start_lfclk();
+
+ // Try to set clock to 1kHz.
+ match rtc.set_prescaler(32) {
+ Ok(()) => trace!("set prescaler!"),
+ Err(x) => trace!("couldn't set prescaler: {:?}", x),
+ }
+ // Make sure event is cleared before start.
+ let _ = rtc.get_event_triggered(RtcInterrupt::Tick, true);
+
+ let mut rtc = rtc.enable_counter();
+ move || handler(&mut rtc)
+}
+
+pub fn millis() -> usize {
+ CLOCK.millis()
+}
+
+fn handler<R>(rtc: &mut Rtc<R, rtc::Started>)
+where
+ R: rtc::Instance,
+{
+ let _ = rtc.get_event_triggered(RtcInterrupt::Tick, true);
+ CLOCK.add(1);
+ log::logger().flush();
+}