Initial commit

This commit is contained in:
pjht 2023-10-01 19:19:19 -05:00
commit f8b331b659
Signed by: pjht
GPG Key ID: CA239FC6934E6F3A
28 changed files with 2061 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
target

90
common/Cargo.lock generated Normal file
View File

@ -0,0 +1,90 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "common"
version = "0.1.0"
dependencies = [
"embedded-hal",
"shared-bus",
"spin",
]
[[package]]
name = "embedded-hal"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
"nb 0.1.3",
"void",
]
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
"nb 1.1.0",
]
[[package]]
name = "nb"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
[[package]]
name = "portable-atomic"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc59d1bcc64fc5d021d67521f818db868368028108d37f0e98d74e33f68297b5"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "shared-bus"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f8438a40b91c8b9531c664e9680c55b92bd78cd6809a8b45b4512b1e5765f2"
dependencies = [
"embedded-hal",
"nb 0.1.3",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "git+https://github.com/mvdnes/spin-rs#5be251f5b52b653c2c40fcdc1089d38114125efa"
dependencies = [
"lock_api",
"portable-atomic",
]
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"

11
common/Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "common"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
embedded-hal = "0.2.7"
shared-bus = { version = "0.2.5" }
spin = {git = "https://github.com/mvdnes/spin-rs", features = ['portable_atomic']}

48
common/src/lcd.rs Normal file
View File

@ -0,0 +1,48 @@
use core::fmt::{Error, Write};
use embedded_hal::blocking::{i2c::Write as I2CWrite, delay::DelayUs};
use spin::Mutex;
pub static DEFAULT_ADDR: u8 = 0x72;
pub struct SerLCD<'a, T, D> {
i2c: T,
addr: u8,
delay: &'a Mutex<D>
}
impl<'a, T, D, E> SerLCD<'a, T, D>
where
T: I2CWrite<Error = E>,
D: DelayUs<u32>,
{
pub fn new(i2c: T, delay: &'a Mutex<D>) -> Self {
Self { i2c, addr: DEFAULT_ADDR, delay }
}
pub fn new_with_addr(i2c: T, addr: u8, delay: &'a Mutex<D>) -> Self {
Self { i2c, addr, delay }
}
pub fn clear(&mut self) -> Result<(), E> {
self.delay.lock().delay_us(500u32);
self.i2c.write(self.addr, b"|-")
}
pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), E> {
self.delay.lock().delay_us(500u32);
self.i2c.write(self.addr, bytes)
}
}
impl<T, D, E> Write for SerLCD<'_, T, D>
where
T: I2CWrite<Error = E>,
D: DelayUs<u32>,
{
fn write_str(&mut self, s: &str) -> core::fmt::Result {
self.delay.lock().delay_us(500u32);
self.i2c.write(self.addr, s.as_bytes()).map_err(|_| Error)
}
}

182
common/src/lib.rs Normal file
View File

@ -0,0 +1,182 @@
#![no_std]
mod lcd;
mod seesaw;
pub mod timer;
use shared_bus::{BusManager, BusMutex};
use timer::Timer;
use core::fmt::{Debug, Write};
use embedded_hal::{
blocking::{i2c::{Read as I2CRead, Write as I2CWrite}, delay::{DelayMs, DelayUs}},
digital::v2::{InputPin, OutputPin},
};
use lcd::SerLCD;
use seesaw::Seesaw;
use spin::Mutex;
const ENCODER_ADDREESS: u8 = 0x36;
const ENCODER_SW_PIN: u8 = 24;
#[derive(Debug, Copy, Clone)]
enum Mode {
Timer,
Focus,
TimePaperDev,
}
impl Mode {
fn next(self) -> Self {
match self {
Self::Timer => Self::Focus,
Self::Focus => Self::TimePaperDev,
Self::TimePaperDev => Self::Timer,
}
}
fn prev(self) -> Self {
match self {
Self::Timer => Self::TimePaperDev,
Self::Focus => Self::Timer,
Self::TimePaperDev => Self::Focus,
}
}
}
pub fn main<
U: Write,
IM: BusMutex<Bus = IB>,
IB: I2CRead<Error = IE> + I2CWrite<Error = IE>,
IE: Debug,
T: Timer,
D: DelayMs<u32> + DelayUs<u32>,
P: InputPin<Error = PE>,
PE: Debug,
R: OutputPin<Error = RE>,
RE: Debug,
>(mut uart: U, i2c: BusManager<IM>, timer: T, delay: D, pedal: P, mut relay: R) -> ! {
let delay = Mutex::new(delay);
write!(&mut uart, "Timer reset").unwrap();
let mut lcd = SerLCD::new(i2c.acquire_i2c(), &delay);
lcd.write_bytes(&[b'|', b'+', 32, 0, 0]).unwrap();
let mut encoder = Seesaw::new(i2c.acquire_i2c(), ENCODER_ADDREESS, &delay);
while lcd.clear().is_err() {} // Wait for the LCD to be ready
encoder
.pin_mode(ENCODER_SW_PIN, seesaw::PinMode::Input)
.unwrap();
let mut time = 0;
let mut mode = Mode::Timer;
'main: loop {
lcd.clear().unwrap();
// The button is active-low, so invert the read value to make it active-high.
let encoder_button = !encoder.digital_read(ENCODER_SW_PIN).unwrap();
let delta = -encoder.get_encoder_delta().unwrap();
if encoder_button {
relay.set_low().unwrap();
write!(&mut lcd, "Mode: {:?}", mode).unwrap();
#[allow(clippy::comparison_chain)]
if delta > 0 {
mode = mode.next();
} else if delta < 0 {
mode = mode.prev();
}
} else {
match mode {
Mode::Focus => {
relay.set_high().unwrap();
write!(&mut lcd, "Focus").unwrap();
}
Mode::Timer => {
if pedal.is_high().unwrap() {
relay.set_high().unwrap();
countdown(time as u64, &mut lcd, "Exposing", &mut encoder, &pedal, &delay, &timer);
relay.set_low().unwrap();
mode = Mode::TimePaperDev;
} else {
time += delta;
if time < 0 {
time = 0;
}
write!(&mut lcd, "Time: {:0>2}:{:0>2}", time / 60, time % 60).unwrap();
}
}
Mode::TimePaperDev => {
delay.lock().delay_ms(2u32);
write!(&mut lcd, "|-Ready dev").unwrap();
while pedal.is_low().unwrap() {
if !encoder.digital_read(ENCODER_SW_PIN).unwrap() {
continue 'main;
}
}
countdown(60 + 30, &mut lcd, "Dev", &mut encoder, &pedal, &delay, &timer);
write!(&mut lcd, "|-Ready stop").unwrap();
while pedal.is_low().unwrap() {
if !encoder.digital_read(ENCODER_SW_PIN).unwrap() {
continue 'main;
}
}
countdown(30, &mut lcd, "Stop", &mut encoder, &pedal, &delay, &timer);
write!(&mut lcd, "|-Ready fix").unwrap();
while pedal.is_low().unwrap() {
if !encoder.digital_read(ENCODER_SW_PIN).unwrap() {
continue 'main;
}
}
countdown(30, &mut lcd, "Fix", &mut encoder, &pedal, &delay, &timer);
mode = Mode::Timer;
}
}
};
delay.lock().delay_ms(100u32);
}
}
fn countdown<
'a,
I: I2CRead<Error = SE> + I2CWrite<Error = SE>,
SE: Debug,
P: InputPin<Error = PE>,
PE: Debug,
D: DelayMs<u32> + DelayUs<u32>,
TM: Timer
>(
seconds: u64,
lcd: &mut SerLCD<'a, I, D>,
name: &str,
encoder: &mut Seesaw<'a, I, D>,
pedal: &P,
delay: &Mutex<D>,
timer: &TM,
) {
let start_time = timer.get_counter();
let finish_time = start_time + (seconds as u64) * 1_000_000;
write!(lcd, "|-{name}: {:0>2}:{:0>2}", seconds / 60, seconds % 60).unwrap();
let mut next_print_time = start_time + 1_000_000;
while timer.get_counter() < finish_time {
let current_time = timer.get_counter();
if next_print_time < timer.get_counter() {
let current_seconds = ((finish_time - current_time) / 1_000_000)
+ if ((finish_time - current_time) % 1_000_000) > 0 {
1
} else {
0
};
write!(
lcd,
"|-{name}: {:0>2}:{:0>2}",
current_seconds / 60,
current_seconds % 60,
)
.unwrap();
next_print_time += 1_000_000;
}
if !encoder.digital_read(ENCODER_SW_PIN).unwrap() && pedal.is_high().unwrap() {
write!(lcd, "|-Canceled, release buttons").unwrap();
while !encoder.digital_read(ENCODER_SW_PIN).unwrap() || pedal.is_high().unwrap() {}
delay.lock().delay_ms(10);
return;
}
delay.lock().delay_ms(10);
}
}

110
common/src/seesaw.rs Normal file
View File

@ -0,0 +1,110 @@
use embedded_hal::blocking::{i2c::{Read, Write}, delay::DelayUs};
mod registers;
use registers::*;
use spin::Mutex;
#[derive(Copy, Clone, Debug)]
pub enum PinMode {
#[allow(unused)]
Output,
Input,
#[allow(unused)]
InputPullup,
#[allow(unused)]
InputPulldown,
}
pub struct Seesaw<'a, T, D> {
i2c: T,
addr: u8,
delay: &'a Mutex<D>,
}
impl<'a, T, D, E> Seesaw<'a, T, D>
where
T: Write<Error = E> + Read<Error = E>,
D: DelayUs<u32>,
{
pub fn new(i2c: T, addr: u8, delay: &'a Mutex<D>) -> Self {
Self { i2c, addr, delay }
}
pub fn pin_mode(&mut self, pin: u8, mode: PinMode) -> Result<(), E> {
if pin >= 32 {
self.pin_mode_bulk(0, 1 << (pin as u32 - 32), mode)
} else {
self.pin_mode_bulk(1 << (pin as u32), 0, mode)
}
}
pub fn digital_read(&mut self, pin: u8) -> Result<bool, E> {
if pin >= 32 {
Ok(self.digital_read_bulk_b(1 << (pin as u32 - 32))? != 0)
} else {
Ok(self.digital_read_bulk(1 << pin as u32)? != 0)
}
}
pub fn digital_read_bulk(&mut self, pins: u32) -> Result<u32, E> {
let mut buf = [0; 4];
self.read(GpioRegisters::Read, &mut buf)?;
Ok(u32::from_be_bytes(buf) & pins)
}
pub fn digital_read_bulk_b(&mut self, pins: u32) -> Result<u32, E> {
let mut buf = [0; 8];
self.read(GpioRegisters::Read, &mut buf)?;
Ok(u32::from_be_bytes(buf[4..8].try_into().unwrap()) & pins)
}
pub fn pin_mode_bulk(&mut self, pinsa: u32, pinsb: u32, mode: PinMode) -> Result<(), E> {
let mut cmd = [0; 8];
cmd[0..4].copy_from_slice(&pinsa.to_be_bytes());
cmd[4..8].copy_from_slice(&pinsb.to_be_bytes());
match mode {
PinMode::Output => self.write(GpioRegisters::Dirset, &cmd),
PinMode::Input => self.write(GpioRegisters::Dirclr, &cmd),
PinMode::InputPullup => {
self.write(GpioRegisters::Dirclr, &cmd)?;
self.write(GpioRegisters::Pullenset, &cmd)?;
self.write(GpioRegisters::Set, &cmd)
}
PinMode::InputPulldown => {
self.write(GpioRegisters::Dirclr, &cmd)?;
self.write(GpioRegisters::Pullenset, &cmd)?;
self.write(GpioRegisters::Clr, &cmd)
}
}
}
pub fn get_encoder_delta(&mut self) -> Result<i32, E> {
let mut buf = [0; 4];
self.read(EncoderRegisters::Delta, &mut buf)?;
Ok(i32::from_be_bytes(buf))
}
pub fn read<R: Into<SeesawRegisters>>(&mut self, reg: R, data: &mut [u8]) -> Result<(), E> {
let (modu, reg) = reg.into().into();
self.delay.lock().delay_us(500u32);
self.i2c.write(self.addr, &[modu, reg])?;
self.delay.lock().delay_us(500u32);
self.i2c.read(self.addr, data)?;
Ok(())
}
pub fn write<R: Into<SeesawRegisters>>(&mut self, reg: R, data: &[u8]) -> Result<(), E> {
let (modu, reg) = reg.into().into();
self.delay.lock().delay_us(500u32);
self.i2c.write(self.addr, &[modu, reg])?;
self.delay.lock().delay_us(500u32);
self.i2c.write(self.addr, data)?;
Ok(())
}
}

View File

@ -0,0 +1,51 @@
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum GpioRegisters {
Dirset = 0x02,
Dirclr = 0x03,
Read = 0x04,
Set = 0x05,
Clr = 0x06,
Toggle = 0x07,
Intenset = 0x08,
Intenclr = 0x09,
Intflag = 0x0A,
Pullenset = 0x0B,
Pullenclr = 0x0C,
}
#[repr(u8)]
#[derive(Copy, Clone, Debug)]
pub enum EncoderRegisters {
Status = 0x0,
Intenset = 0x10,
Intenclr = 0x20,
Position = 0x30,
Delta = 0x40,
}
pub enum SeesawRegisters {
Gpio(GpioRegisters),
Encoder(EncoderRegisters),
}
impl From<EncoderRegisters> for SeesawRegisters {
fn from(v: EncoderRegisters) -> Self {
Self::Encoder(v)
}
}
impl From<GpioRegisters> for SeesawRegisters {
fn from(v: GpioRegisters) -> Self {
Self::Gpio(v)
}
}
impl From<SeesawRegisters> for (u8, u8) {
fn from(r: SeesawRegisters) -> Self {
match r {
SeesawRegisters::Gpio(r) => (0x01, r as u8),
SeesawRegisters::Encoder(r) => (0x11, r as u8),
}
}
}

3
common/src/timer.rs Normal file
View File

@ -0,0 +1,3 @@
pub trait Timer {
fn get_counter(&self) -> u64;
}

405
dev/Cargo.lock generated Normal file
View File

@ -0,0 +1,405 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.71"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "cassowary"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "common"
version = "0.1.0"
dependencies = [
"embedded-hal",
"shared-bus",
"spin",
]
[[package]]
name = "crossterm"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
dependencies = [
"bitflags",
"crossterm_winapi",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm"
version = "0.26.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a84cda67535339806297f1b331d6dd6320470d2a0fe65381e79ee9e156dd3d13"
dependencies = [
"bitflags",
"crossterm_winapi",
"libc",
"mio",
"parking_lot",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ae1b35a484aa10e07fe0638d02301c5ad24de82d310ccbd2f3693da5f09bf1c"
dependencies = [
"winapi",
]
[[package]]
name = "dev"
version = "0.1.0"
dependencies = [
"anyhow",
"common",
"crossterm 0.26.1",
"embedded-hal",
"itertools",
"parking_lot",
"shared-bus",
"tui",
]
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "embedded-hal"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
"nb 0.1.3",
"void",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "libc"
version = "0.2.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1"
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "mio"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys",
]
[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
"nb 1.1.0",
]
[[package]]
name = "nb"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "parking_lot"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]]
name = "portable-atomic"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc59d1bcc64fc5d021d67521f818db868368028108d37f0e98d74e33f68297b5"
[[package]]
name = "redox_syscall"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
dependencies = [
"bitflags",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "shared-bus"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f8438a40b91c8b9531c664e9680c55b92bd78cd6809a8b45b4512b1e5765f2"
dependencies = [
"embedded-hal",
"nb 0.1.3",
"once_cell",
]
[[package]]
name = "signal-hook"
version = "0.3.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "732768f1176d21d09e076c23a93123d40bba92d50c4058da34d45c8de8e682b9"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1"
dependencies = [
"libc",
]
[[package]]
name = "smallvec"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
[[package]]
name = "spin"
version = "0.9.8"
source = "git+https://github.com/mvdnes/spin-rs#5be251f5b52b653c2c40fcdc1089d38114125efa"
dependencies = [
"lock_api",
"portable-atomic",
]
[[package]]
name = "tui"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1"
dependencies = [
"bitflags",
"cassowary",
"crossterm 0.25.0",
"unicode-segmentation",
"unicode-width",
]
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
[[package]]
name = "windows_i686_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
[[package]]
name = "windows_i686_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
[[package]]
name = "windows_x86_64_gnu"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
[[package]]
name = "windows_x86_64_msvc"
version = "0.42.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"

16
dev/Cargo.toml Normal file
View File

@ -0,0 +1,16 @@
[package]
name = "dev"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.71"
common = { version = "0.1.0", path = "../common" }
crossterm = "0.26.1"
embedded-hal = "0.2.7"
itertools = "0.10.5"
parking_lot = "0.12.1"
shared-bus = { version = "0.2.5", features = [ "std" ] }
tui = "0.19.0"

33
dev/src/arc_wrp.rs Normal file
View File

@ -0,0 +1,33 @@
use std::{
ops::{Deref, DerefMut},
sync::Arc,
};
pub struct ArcWrp<T>(Arc<T>);
impl<T> ArcWrp<T> {
pub fn new(data: T) -> Self {
Self(Arc::new(data))
}
}
impl<T> Deref for ArcWrp<T> {
type Target = Arc<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for ArcWrp<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
// Must use instead of derive or arc_wrp.clone() calls clone on the inner Arc
impl<T> Clone for ArcWrp<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}

24
dev/src/delay.rs Normal file
View File

@ -0,0 +1,24 @@
use std::time::Duration;
use embedded_hal::blocking::delay::{DelayMs, DelayUs};
pub struct Delay;
impl Delay {
pub fn new() -> Self {
Self
}
}
impl DelayMs<u32> for Delay {
fn delay_ms(&mut self, ms: u32) {
std::thread::sleep(Duration::from_millis(ms as u64));
}
}
impl DelayUs<u32> for Delay {
fn delay_us(&mut self, us: u32) {
std::thread::sleep(Duration::from_micros(us as u64));
}
}

78
dev/src/encoder.rs Normal file
View File

@ -0,0 +1,78 @@
use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use parking_lot::Mutex;
struct EncoderMutState {
read_buf: Vec<u8>,
command: Option<(u8, u8)>,
}
pub struct Encoder {
mut_state: Mutex<EncoderMutState>,
delta: AtomicI32,
button: AtomicBool,
}
impl Encoder {
pub fn new() -> Self {
Self {
mut_state: Mutex::new(EncoderMutState {
read_buf: Vec::new(),
command: None,
}),
delta: AtomicI32::new(0),
button: AtomicBool::new(false),
}
}
pub fn write(&self, bytes: &[u8]) {
let mut mut_state = self.mut_state.lock();
if let Some(_command) = mut_state.command {
mut_state.command = None;
} else {
mut_state.read_buf.clear();
mut_state.command = Some((bytes[0], bytes[1]));
match (bytes[0], bytes[1]) {
// GPIO read
(0x01, 0x04) => {
let button_resp = self.button.load(Ordering::Relaxed);
if button_resp {
mut_state
.read_buf
.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]);
} else {
mut_state
.read_buf
.extend_from_slice(&[1, 0, 0, 0, 0, 0, 0, 0]);
}
mut_state.command = None
}
// Encoder delta
(0x11, 0x40) => {
mut_state
.read_buf
.extend_from_slice(&(self.delta.swap(0, Ordering::Relaxed)).to_be_bytes());
mut_state.command = None;
}
_ => (),
}
}
}
pub fn read(&self, buffer: &mut [u8]) {
let mut mut_state = self.mut_state.lock();
let mut buf_remaining = buffer.len();
while mut_state.read_buf.len() > 0 && buf_remaining > 0 {
buffer[buffer.len() - buf_remaining] = mut_state.read_buf.remove(0);
buf_remaining -= 1;
}
}
pub fn delta(&self) -> &AtomicI32 {
&self.delta
}
pub fn button(&self) -> &AtomicBool {
&self.button
}
}

57
dev/src/i2c.rs Normal file
View File

@ -0,0 +1,57 @@
use std::sync::Arc;
use crate::{encoder::Encoder, lcd::Lcd};
use embedded_hal::blocking::i2c::{Read as I2CRead, Write as I2CWrite};
pub struct I2C {
lcd: Lcd,
encoder: Arc<Encoder>,
}
impl I2C {
pub fn new(lcd: Lcd, encoder: Arc<Encoder>) -> Self {
Self { lcd, encoder }
}
}
impl I2CRead for I2C {
type Error = ();
fn read(&mut self, address: u8, buffer: &mut [u8]) -> Result<(), Self::Error> {
#[allow(clippy::single_match)]
match address {
// Ignore LCD read
0x72 => (),
0x36 => {
self.encoder.read(buffer);
}
_ => panic!(
"I2C read from unknown device at address {:#x} of {} bytes",
address,
buffer.len()
),
}
Ok(())
}
}
impl I2CWrite for I2C {
type Error = ();
fn write(&mut self, address: u8, bytes: &[u8]) -> Result<(), Self::Error> {
match address {
0x72 => {
self.lcd.write(bytes);
}
0x36 => {
self.encoder.write(bytes);
}
_ => panic!(
"I2C write to unknown device at address {:#x} with data {:?}",
address, bytes
),
}
Ok(())
}
}

58
dev/src/lcd.rs Normal file
View File

@ -0,0 +1,58 @@
use std::sync::{mpsc::Sender, Arc};
use parking_lot::Mutex;
use crate::Message;
struct LcdCommon {
cursor: Mutex<u8>,
text: Mutex<[u8; 0x68]>,
}
#[derive(Clone)]
pub struct Lcd {
channel: Sender<Message>,
common: Arc<LcdCommon>,
}
impl Lcd {
pub fn new(channel: Sender<Message>) -> Self {
Self {
channel,
common: Arc::new(LcdCommon {
cursor: Mutex::new(0),
text: Mutex::new([b' '; 0x68]),
}),
}
}
pub fn write(&self, bytes: &[u8]) {
let mut cursor = self.common.cursor.lock();
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'|' {
match bytes[i + 1] {
b'|' => todo!(),
b'-' => {
*self.common.text.lock() = [b' '; 0x68];
*cursor = 0;
}
_ => (),
}
if bytes[i] & 0x80 == 0x80 {
*cursor = bytes[i] & 0x7F;
}
i += 1;
} else {
self.common.text.lock()[*cursor as usize] = bytes[i];
*cursor += 1;
}
i += 1;
}
self.channel.send(Message::Update).unwrap();
}
pub fn text(&self) -> &Mutex<[u8; 0x68]> {
&self.common.text
}
}

168
dev/src/main.rs Normal file
View File

@ -0,0 +1,168 @@
mod arc_wrp;
mod delay;
mod encoder;
mod i2c;
mod lcd;
mod pedal;
mod relay;
mod timer;
mod uart;
use std::{
io,
sync::{atomic::Ordering, mpsc::channel, Arc},
};
use arc_wrp::ArcWrp;
use crossterm::{
event::{Event, KeyCode},
terminal::{disable_raw_mode, enable_raw_mode},
};
use delay::Delay;
use encoder::Encoder;
use i2c::I2C;
use lcd::Lcd;
use pedal::Pedal;
use relay::Relay;
use shared_bus::BusManagerStd;
use timer::Timer;
use tui::{
backend::CrosstermBackend,
layout::Rect,
widgets::{Block, Borders, Paragraph},
Terminal,
};
use uart::Uart;
pub enum Message {
Update,
Quit,
}
fn main() -> anyhow::Result<()> {
enable_raw_mode()?;
let stdout = io::stdout();
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
let (tx, rx) = channel();
let uart = ArcWrp::new(Uart::new());
let lcd = Lcd::new(tx.clone());
let encoder = Arc::new(Encoder::new());
let i2c = BusManagerStd::new(I2C::new(lcd.clone(), encoder.clone()));
let timer = ArcWrp::new(Timer::new());
let delay = Delay::new();
let pedal = ArcWrp::new(Pedal::new());
let relay = Relay::new(tx.clone());
{
let timer = timer.clone();
let uart = uart.clone();
let pedal = pedal.clone();
let relay = relay.clone();
std::thread::spawn(move || common::main(uart, i2c, timer, delay, pedal, relay));
}
{
let tx = tx.clone();
let pedal = pedal.clone();
let encoder = encoder.clone();
let timer = timer.clone();
std::thread::spawn(move || loop {
let event = crossterm::event::read().unwrap();
if let Event::Key(event) = event {
match event.code {
KeyCode::Char('q') => {
tx.send(Message::Quit).unwrap();
continue;
}
KeyCode::Right => {
encoder.delta().fetch_sub(1, Ordering::Relaxed);
}
KeyCode::Left => {
encoder.delta().fetch_add(1, Ordering::Relaxed);
}
KeyCode::Char('e') => {
let old = encoder.button().load(Ordering::Relaxed);
encoder.button().store(!old, Ordering::Relaxed);
}
KeyCode::Char('p') => {
pedal.state().store(true, Ordering::Relaxed);
}
KeyCode::Char('n') => {
if timer.time_mult().load(Ordering::Relaxed) > 1 {
timer.time_mult().fetch_sub(1, Ordering::Relaxed);
}
}
KeyCode::Char('m') => {
timer.time_mult().fetch_add(1, Ordering::Relaxed);
}
_ => (),
};
}
tx.send(Message::Update).unwrap();
});
}
terminal.clear()?;
let term_size = terminal.size()?;
let uart_height = term_size.height;
let uart_width = term_size.width - 22;
let mut lcd_render = String::new();
loop {
terminal.draw(|f| {
if let Some(lcd_text) = lcd.text().try_lock() {
lcd_render.clear();
for c in &lcd_text[0x0..0x14] {
lcd_render.push(*c as char);
}
for c in &lcd_text[0x40..0x54] {
lcd_render.push(*c as char);
}
for c in &lcd_text[0x14..0x28] {
lcd_render.push(*c as char);
}
for c in &lcd_text[0x54..0x68] {
lcd_render.push(*c as char);
}
}
f.render_widget(
Paragraph::new(&*lcd_render)
.block(Block::default().title("LCD").borders(Borders::ALL)),
Rect::new(0, 0, 20, 6),
);
let relay_text = if relay.state().load(Ordering::Relaxed) {
"Enlarger on"
} else {
"Enlarger off"
};
f.render_widget(Paragraph::new(relay_text), Rect::new(0, 7, 12, 1));
f.render_widget(
Paragraph::new(format!(
"Time mult: x{}",
timer.time_mult().load(Ordering::Relaxed)
)),
Rect::new(0, 9, 22, 1),
);
let enc_button_text = if encoder.button().load(Ordering::Relaxed) {
"Encoder pressed"
} else {
"Encoder released"
};
f.render_widget(Paragraph::new(enc_button_text), Rect::new(0, 11, 16, 1));
f.render_widget(Paragraph::new(format!("Encoder delta: {}", encoder.delta().load(Ordering::Relaxed))), Rect::new(0, 13, 22, 1));
f.render_widget(
Paragraph::new(&**uart.out().lock())
.block(Block::default().title("UART").borders(Borders::ALL)),
Rect::new(22, 0, uart_width, uart_height),
);
})?;
match rx.recv() {
Ok(m) => match m {
Message::Update => (),
Message::Quit => break,
},
Err(_) => panic!(),
}
}
terminal.clear()?;
terminal.set_cursor(0, 0)?;
disable_raw_mode()?;
Ok(())
}

33
dev/src/pedal.rs Normal file
View File

@ -0,0 +1,33 @@
use std::sync::atomic::{AtomicBool, Ordering};
use embedded_hal::digital::v2::InputPin;
use crate::arc_wrp::ArcWrp;
pub struct Pedal {
state: AtomicBool,
}
impl Pedal {
pub fn new() -> Self {
Self {
state: AtomicBool::new(false),
}
}
pub fn state(&self) -> &AtomicBool {
&self.state
}
}
impl InputPin for ArcWrp<Pedal> {
type Error = ();
fn is_high(&self) -> Result<bool, Self::Error> {
Ok(self.state.swap(false, Ordering::Relaxed))
}
fn is_low(&self) -> Result<bool, Self::Error> {
self.is_high().map(|x| !x)
}
}

44
dev/src/relay.rs Normal file
View File

@ -0,0 +1,44 @@
use std::sync::{
atomic::{AtomicBool, Ordering},
mpsc::Sender,
Arc,
};
use embedded_hal::digital::v2::OutputPin;
use crate::Message;
#[derive(Clone)]
pub struct Relay {
channel: Sender<Message>,
state: Arc<AtomicBool>,
}
impl Relay {
pub fn new(channel: Sender<Message>) -> Self {
Self {
channel,
state: Arc::new(AtomicBool::new(false)),
}
}
pub fn state(&self) -> &AtomicBool {
self.state.as_ref()
}
}
impl OutputPin for Relay {
type Error = ();
fn set_low(&mut self) -> Result<(), Self::Error> {
self.state.store(false, Ordering::Relaxed);
self.channel.send(Message::Update).unwrap();
Ok(())
}
fn set_high(&mut self) -> Result<(), Self::Error> {
self.state.store(true, Ordering::Relaxed);
self.channel.send(Message::Update).unwrap();
Ok(())
}
}

31
dev/src/timer.rs Normal file
View File

@ -0,0 +1,31 @@
use std::{
sync::atomic::{AtomicU32, Ordering},
time::Instant,
};
use crate::arc_wrp::ArcWrp;
pub struct Timer {
start_time: Instant,
time_mult: AtomicU32,
}
impl Timer {
pub fn new() -> Self {
Self {
start_time: Instant::now(),
time_mult: AtomicU32::new(1),
}
}
pub fn time_mult(&self) -> &AtomicU32 {
&self.time_mult
}
}
impl common::timer::Timer for ArcWrp<Timer> {
fn get_counter(&self) -> u64 {
Instant::now().duration_since(self.start_time).as_micros() as u64
* self.time_mult.load(Ordering::Relaxed) as u64
}
}

28
dev/src/uart.rs Normal file
View File

@ -0,0 +1,28 @@
use std::fmt::Write;
use parking_lot::Mutex;
use crate::arc_wrp::ArcWrp;
pub struct Uart {
out: Mutex<String>,
}
impl Uart {
pub fn new() -> Self {
Self {
out: Mutex::new(String::new()),
}
}
pub fn out(&self) -> &Mutex<String> {
&self.out
}
}
impl Write for ArcWrp<Uart> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
*self.out.lock() += s;
Ok(())
}
}

15
embedded/.cargo/config Normal file
View File

@ -0,0 +1,15 @@
[build]
target = "thumbv6m-none-eabi" # Adafruit QT Py - SAMD21
[target.thumbv6m-none-eabi]
runner = 'elf2uf2-rs -d'
rustflags = [
# This is needed if your flash or ram addresses are not aligned to 0x10000 in memory.x
# See https://github.com/rust-embedded/cortex-m-quickstart/pull/95
"-C", "link-arg=--nmagic",
"-C", "link-arg=-Tlink.x",
"--cfg", "portable_atomic_unsafe_assume_single_core",
]

412
embedded/Cargo.lock generated Normal file
View File

@ -0,0 +1,412 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "adafruit-qt-py-rp2040"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e36e061b89c1354cab7a96173730928d67e9970ad509cd295af724abb30920b6"
dependencies = [
"cortex-m",
"cortex-m-rt",
"rp2040-boot2",
"rp2040-hal",
]
[[package]]
name = "arrayvec"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bare-metal"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
dependencies = [
"rustc_version",
]
[[package]]
name = "bitfield"
version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
[[package]]
name = "common"
version = "0.1.0"
dependencies = [
"embedded-hal",
"shared-bus",
"spin",
]
[[package]]
name = "cortex-m"
version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
dependencies = [
"bare-metal",
"bitfield",
"embedded-hal",
"volatile-register",
]
[[package]]
name = "cortex-m-rt"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee84e813d593101b1723e13ec38b6ab6abbdbaaa4546553f5395ed274079ddb1"
dependencies = [
"cortex-m-rt-macros",
]
[[package]]
name = "cortex-m-rt-macros"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0f6f3e36f203cfedbc78b357fb28730aa2c6dc1ab060ee5c2405e843988d3c7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "crc-any"
version = "2.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774646b687f63643eb0f4bf13dc263cb581c8c9e57973b6ddf78bda3994d88df"
dependencies = [
"debug-helper",
]
[[package]]
name = "critical-section"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6548a0ad5d2549e111e1f6a11a6c2e2d00ce6a3dafe22948d67c2b443f775e52"
[[package]]
name = "debug-helper"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e"
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "embedded-dma"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "994f7e5b5cb23521c22304927195f236813053eb9c065dd2226a32ba64695446"
dependencies = [
"stable_deref_trait",
]
[[package]]
name = "embedded-hal"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
dependencies = [
"nb 0.1.3",
"void",
]
[[package]]
name = "fugit"
version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ab17bb279def6720d058cb6c052249938e7f99260ab534879281a95367a87e5"
dependencies = [
"gcd",
]
[[package]]
name = "gcd"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "nb"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
dependencies = [
"nb 1.1.0",
]
[[package]]
name = "nb"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d"
[[package]]
name = "num_enum"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9"
dependencies = [
"num_enum_derive",
]
[[package]]
name = "num_enum_derive"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "paste"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79"
[[package]]
name = "pio"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76e09694b50f89f302ed531c1f2a7569f0be5867aee4ab4f8f729bbeec0078e3"
dependencies = [
"arrayvec",
"num_enum",
"paste",
]
[[package]]
name = "portable-atomic"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc59d1bcc64fc5d021d67521f818db868368028108d37f0e98d74e33f68297b5"
[[package]]
name = "proc-macro2"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6aeca18b86b413c660b781aa319e4e2648a3e6f9eadc9b47e9038e6fe9f3451b"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ab9c7eadfd8df19006f1cf1a4aed13540ed5cbc047010ece5826e10825488"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rp2040-boot2"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c773ec49b836077aa144b58dc7654a243e1eecdb6cf0d25361ae7c7600fabd8"
dependencies = [
"crc-any",
]
[[package]]
name = "rp2040-hal"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1369bb84862d7f69391a96606b2f29a00bfce7f29a749e23d5f01fc3f607ada0"
dependencies = [
"cortex-m",
"critical-section",
"embedded-dma",
"embedded-hal",
"fugit",
"itertools",
"nb 1.1.0",
"paste",
"pio",
"rand_core",
"rp2040-hal-macros",
"rp2040-pac",
"usb-device",
"vcell",
"void",
]
[[package]]
name = "rp2040-hal-macros"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86479063e497efe1ae81995ef9071f54fd1c7427e04d6c5b84cde545ff672a5e"
dependencies = [
"cortex-m-rt",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "rp2040-pac"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9192cafbb40d717c9e0ddf767aaf9c69fee1b4e48d22ed853b57b11f6d9f3d7e"
dependencies = [
"cortex-m",
"cortex-m-rt",
"vcell",
]
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
dependencies = [
"semver",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
dependencies = [
"semver-parser",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
[[package]]
name = "shared-bus"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05f8438a40b91c8b9531c664e9680c55b92bd78cd6809a8b45b4512b1e5765f2"
dependencies = [
"embedded-hal",
"nb 0.1.3",
]
[[package]]
name = "spin"
version = "0.9.8"
source = "git+https://github.com/mvdnes/spin-rs#5be251f5b52b653c2c40fcdc1089d38114125efa"
dependencies = [
"lock_api",
"portable-atomic",
]
[[package]]
name = "stable_deref_trait"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "timer"
version = "0.1.0"
dependencies = [
"adafruit-qt-py-rp2040",
"common",
"cortex-m",
"cortex-m-rt",
"embedded-hal",
"fugit",
"shared-bus",
]
[[package]]
name = "unicode-ident"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15811caf2415fb889178633e7724bad2509101cde276048e013b9def5e51fa0"
[[package]]
name = "usb-device"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f6cc3adc849b5292b4075fc0d5fdcf2f24866e88e336dd27a8943090a520508"
[[package]]
name = "vcell"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
[[package]]
name = "void"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
[[package]]
name = "volatile-register"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6"
dependencies = [
"vcell",
]

15
embedded/Cargo.toml Normal file
View File

@ -0,0 +1,15 @@
[package]
name = "timer"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
adafruit-qt-py-rp2040 = "0.6.0"
common = { version = "0.1.0", path = "../common" }
cortex-m = "0.7.7"
cortex-m-rt = "0.7.3"
embedded-hal = "0.2.7"
fugit = "0.3.6"
shared-bus = { version = "0.2.5" }

16
embedded/build.rs Normal file
View File

@ -0,0 +1,16 @@
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::PathBuf;
fn main() {
if env::var_os("CARGO_FEATURE_RT").is_some() {
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());
println!("cargo:rerun-if-changed=memory.x");
}
println!("cargo:rerun-if-changed=build.rs");
}

36
embedded/memory.x Normal file
View File

@ -0,0 +1,36 @@
MEMORY {
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
/*
* RAM consists of 4 banks, SRAM0-SRAM3, with a striped mapping.
* This is usually good for performance, as it distributes load on
* those banks evenly.
*/
RAM : ORIGIN = 0x20000000, LENGTH = 256K
/*
* RAM banks 4 and 5 use a direct mapping. They can be used to have
* memory areas dedicated for some specific job, improving predictability
* of access times.
* Example: Separate stacks for core0 and core1.
*/
SRAM4 : ORIGIN = 0x20040000, LENGTH = 4k
SRAM5 : ORIGIN = 0x20041000, LENGTH = 4k
/* SRAM banks 0-3 can also be accessed directly. However, those ranges
alias with the RAM mapping, above. So don't use them at the same time!
SRAM0 : ORIGIN = 0x21000000, LENGTH = 64k
SRAM1 : ORIGIN = 0x21010000, LENGTH = 64k
SRAM2 : ORIGIN = 0x21020000, LENGTH = 64k
SRAM3 : ORIGIN = 0x21030000, LENGTH = 64k
*/
}
EXTERN(BOOT2_FIRMWARE)
SECTIONS {
/* ### Boot loader */
.boot2 ORIGIN(BOOT2) :
{
KEEP(*(.boot2));
} > BOOT2
} INSERT BEFORE .text;

73
embedded/src/main.rs Normal file
View File

@ -0,0 +1,73 @@
#![no_std]
#![no_main]
mod panic_handler;
mod timer;
use adafruit_qt_py_rp2040::{
hal::{
clocks,
sio,
uart::{UartPeripheral, UartConfig, DataBits, StopBits},
Clock, Timer, Watchdog, I2C,
},
pac, entry,
};
use cortex_m::delay::Delay as CortexDelay;
use fugit::RateExtU32;
use shared_bus::BusManagerSimple;
use timer::TimerWrp;
use common::{self, main};
const XTAL_FREQ_HZ: u32 = 12_000_000;
#[entry]
fn startup() -> ! {
let core_peripherals = pac::CorePeripherals::take().unwrap();
let mut peripherals = pac::Peripherals::take().unwrap();
let mut watchdog = Watchdog::new(peripherals.WATCHDOG);
let clocks = clocks::init_clocks_and_plls(
XTAL_FREQ_HZ,
peripherals.XOSC,
peripherals.CLOCKS,
peripherals.PLL_SYS,
peripherals.PLL_USB,
&mut peripherals.RESETS,
&mut watchdog,
)
.unwrap_or_else(|_| panic!("Failed to init clocks"));
let sio = sio::Sio::new(peripherals.SIO);
let pins = adafruit_qt_py_rp2040::Pins::new(
peripherals.IO_BANK0,
peripherals.PADS_BANK0,
sio.gpio_bank0,
&mut peripherals.RESETS,
);
let uart = UartPeripheral::new(
peripherals.UART1,
(
pins.tx.into_mode(),
pins.rx.into_mode(),
),
&mut peripherals.RESETS,
)
.enable(UartConfig::new(9600u32.Hz(), DataBits::Eight, None, StopBits::One), clocks.peripheral_clock.freq())
.unwrap();
let i2c = BusManagerSimple::new(I2C::i2c1(
peripherals.I2C1,
pins.sda1.into_mode(),
pins.scl1.into_mode(),
100u32.kHz(),
&mut peripherals.RESETS,
&clocks.system_clock,
));
let timer = TimerWrp(Timer::new(peripherals.TIMER, &mut peripherals.RESETS));
let delay = CortexDelay::new(
core_peripherals.SYST,
clocks.system_clock.freq().to_Hz(),
);
let pedal = pins.a0.into_pull_down_input();
let relay = pins.a1.into_push_pull_output();
main(uart, i2c, timer, delay, pedal, relay);
}

View File

@ -0,0 +1,9 @@
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo<'_>) -> ! {
// println!("{}", info);
loop {
cortex_m::asm::wfi()
}
}

14
embedded/src/timer.rs Normal file
View File

@ -0,0 +1,14 @@
use adafruit_qt_py_rp2040::hal::Timer as QtTimer;
use common::timer::Timer;
use fugit::Instant;
pub struct TimerWrp(pub QtTimer);
impl Timer for TimerWrp {
fn get_counter(&self) -> u64 {
// Confirm that the ratio of ticks is as expected by explicitly specifying the type
let instant: Instant<u64, 1, 1_000_000> = QtTimer::get_counter(&self.0);
instant.ticks()
}
}