From 82d4a184a2a4bfbd89c8b4bf1e4be346f2f0f84b Mon Sep 17 00:00:00 2001 From: Victor Timofei Date: Sat, 23 Apr 2022 23:48:42 +0300 Subject: [PATCH] Initial commit --- .cargo/config | 4 ++ .gitignore | 4 ++ Cargo.toml | 30 ++++++++++ Embed.toml | 12 ++++ README.md | 13 +++++ build.rs | 30 ++++++++++ memory.x | 6 ++ src/main.rs | 134 ++++++++++++++++++++++++++++++++++++++++++++ src/serial_setup.rs | 46 +++++++++++++++ 9 files changed, 279 insertions(+) create mode 100644 .cargo/config create mode 100644 .gitignore create mode 100644 Cargo.toml create mode 100644 Embed.toml create mode 100644 README.md create mode 100644 build.rs create mode 100644 memory.x create mode 100644 src/main.rs create mode 100644 src/serial_setup.rs diff --git a/.cargo/config b/.cargo/config new file mode 100644 index 0000000..6260c57 --- /dev/null +++ b/.cargo/config @@ -0,0 +1,4 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +rustflags = [ + "-C", "link-arg=-Tlink.x", +] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..484fe6c --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.gdb_history +/template +Cargo.lock +target/ diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..d6a86e1 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,30 @@ +[package] +authors = ["Henrik Böving "] +edition = "2018" +name = "i2c" +version = "0.1.0" + +[dependencies.microbit-v2] +version = "0.12.0" +git = "https://github.com/nrf-rs/microbit/" +optional = true + + +[dependencies.microbit] +version = "0.12.0" +git = "https://github.com/nrf-rs/microbit/" +optional = true + +[dependencies] +cortex-m = "0.7.3" +cortex-m-rt = "0.7.0" +rtt-target = { version = "0.3.1", features = ["cortex-m"] } +panic-rtt-target = { version = "0.1.2", features = ["cortex-m"] } +nb = "1.0.0" +heapless = "0.7.10" +lsm303agr = "0.2.2" +embedded-hal = "0.2.6" + +[features] +v2 = ["microbit-v2"] +v1 = ["microbit"] diff --git a/Embed.toml b/Embed.toml new file mode 100644 index 0000000..f5117ac --- /dev/null +++ b/Embed.toml @@ -0,0 +1,12 @@ +[default.general] +# chip = "nrf52833_xxAA" # uncomment this line for micro:bit V2 +# chip = "nrf51822_xxAA" # uncomment this line for micro:bit V1 + +[default.reset] +halt_afterwards = false + +[default.rtt] +enabled = true + +[default.gdb] +enabled = false diff --git a/README.md b/README.md new file mode 100644 index 0000000..f6be343 --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +# mbsh - micro:bit shell + +A UART interactive shell for the BBC micro:bit. + +## Supported commands + +- `clear`: Clears the terminal +- `accelerometer`: Returns the accelerometer data +- `magnetometer`: Returns the magnetometer data + +## Demo + +[![asciicast](https://asciinema.org/a/EYeh6pOV8neKA5e4HE2SYI5OQ.svg)](https://asciinema.org/a/EYeh6pOV8neKA5e4HE2SYI5OQ) diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..c8d8c91 --- /dev/null +++ b/build.rs @@ -0,0 +1,30 @@ +//! 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, +//! a rebuild of the application with new memory settings is ensured after updating `memory.x`. + +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"); +} diff --git a/memory.x b/memory.x new file mode 100644 index 0000000..9e2ab65 --- /dev/null +++ b/memory.x @@ -0,0 +1,6 @@ +MEMORY +{ + /* NOTE K = KiBi = 1024 bytes */ + FLASH : ORIGIN = 0x00000000, LENGTH = 256K + RAM : ORIGIN = 0x20000000, LENGTH = 16K +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..d4e7cc3 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,134 @@ +#![no_main] +#![no_std] + +use cortex_m_rt::entry; +use heapless::Vec; +use rtt_target::{rtt_init_print, rprintln}; +use panic_rtt_target as _; +use core::fmt::Write; + +use microbit::hal::prelude::*; + +#[cfg(feature = "v1")] +use microbit::{ + hal::twi, + pac::twi0::frequency::FREQUENCY_A, +}; + +#[cfg(feature = "v2")] +use microbit::{ + hal::twim, + pac::twim0::frequency::FREQUENCY_A, +}; + +use lsm303agr::{ + AccelOutputDataRate, Lsm303agr, +}; + +#[cfg(feature = "v1")] +use microbit::{ + hal::prelude::*, + hal::uart, + hal::uart::{Baudrate, Parity}, +}; + +#[cfg(feature = "v2")] +use microbit::{ + hal::prelude::*, + hal::uarte, + hal::uarte::{Baudrate, Parity}, +}; + +#[cfg(feature = "v2")] +mod serial_setup; +#[cfg(feature = "v2")] +use serial_setup::UartePort; + +#[entry] +fn main() -> ! { + rtt_init_print!(); + let board = microbit::Board::take().unwrap(); + + + #[cfg(feature = "v1")] + let mut i2c = { twi::Twi::new(board.TWI0, board.i2c.into(), FREQUENCY_A::K100) }; + + #[cfg(feature = "v2")] + let mut i2c = { twim::Twim::new(board.TWIM0, board.i2c_internal.into(), FREQUENCY_A::K100) }; + + #[cfg(feature = "v1")] + let mut serial = { + uart::Uart::new( + board.UART0, + board.uart.into(), + Parity::EXCLUDED, + Baudrate::BAUD115200, + ) + }; + + #[cfg(feature = "v2")] + let mut serial = { + let serial = uarte::Uarte::new( + board.UARTE0, + board.uart.into(), + Parity::EXCLUDED, + Baudrate::BAUD115200, + ); + UartePort::new(serial) + }; + + let mut sensor = Lsm303agr::new_with_i2c(i2c); + sensor.init().unwrap(); + sensor.set_accel_odr(AccelOutputDataRate::Hz50); + + let mut buffer: Vec = Vec::new(); + + write!(serial, "{}[2J$ ", 27 as char).unwrap(); + + loop { + let byte = nb::block!(serial.read()).unwrap(); + nb::block!(serial.write(byte)).unwrap(); + + if byte == b'\r' { + nb::block!(serial.write(b'\n')).unwrap(); + while let Some(b' ') = buffer.last() { + buffer.pop(); + } + let cmd = core::str::from_utf8(buffer.as_slice()); + match cmd { + core::result::Result::Ok("accelerometer") => { + rprintln!("INFO: Calculating accelerometer"); + let data = sensor.accel_data(); + match data { + Result::Ok(data) => write!(serial, "x: {}, y: {}, z: {}\r\n", data.x, data.y, data.z).unwrap(), + Result::Err(e) => { + rprintln!("ERR: {:?}", e); + write!(serial, "ERR: {:?}", e).unwrap(); + }, + } + }, + core::result::Result::Ok("magnetometer") => { + rprintln!("INFO: Calculating magnetometer"); + let data = nb::block!(sensor.mag_data()).unwrap(); + write!(serial, "x: {}, y: {}, z: {}\r\n", data.x, data.y, data.z).unwrap(); + + }, + core::result::Result::Ok("clear") => { + write!(serial, "{}[2J", 27 as char).unwrap(); + }, + core::result::Result::Ok("") => {}, + core::result::Result::Ok(cmd) => write!(serial, "Unknown command: `{}`\r\n", cmd).unwrap(), + core::result::Result::Err(e) => rprintln!("ERR: couldn't unwrap cmd: {}\r\n", e), + } + + buffer.clear(); + write!(serial, "$ ").unwrap(); + } else if byte == 8 { + buffer.pop(); + } else if !(buffer.len() == 0 && byte == b' ') { + buffer.push(byte); + } + + nb::block!(serial.flush()).unwrap(); + } +} diff --git a/src/serial_setup.rs b/src/serial_setup.rs new file mode 100644 index 0000000..eb3997a --- /dev/null +++ b/src/serial_setup.rs @@ -0,0 +1,46 @@ +use core::fmt; +use embedded_hal::blocking::serial as bserial; +use embedded_hal::serial; +use microbit::hal::uarte::{Error, Instance, Uarte, UarteRx, UarteTx}; + +static mut TX_BUF: [u8; 1] = [0; 1]; +static mut RX_BUF: [u8; 1] = [0; 1]; + +pub struct UartePort(UarteTx, UarteRx); + +impl UartePort { + pub fn new(serial: Uarte) -> UartePort { + let (tx, rx) = serial + .split(unsafe { &mut TX_BUF }, unsafe { &mut RX_BUF }) + .unwrap(); + UartePort(tx, rx) + } +} + +impl fmt::Write for UartePort { + fn write_str(&mut self, s: &str) -> fmt::Result { + self.0.write_str(s) + } +} + +impl serial::Write for UartePort { + type Error = Error; + + fn write(&mut self, b: u8) -> nb::Result<(), Self::Error> { + self.0.write(b) + } + + fn flush(&mut self) -> nb::Result<(), Self::Error> { + self.0.flush() + } +} + +impl bserial::write::Default for UartePort {} + +impl serial::Read for UartePort { + type Error = Error; + + fn read(&mut self) -> nb::Result { + self.1.read() + } +}