Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions hardware_code/.cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

[target.thumbv6m-none-eabi]
runner = "probe-rs run --chip RP2040 --protocol swd"
rustflags = [
"-C", "link-arg=--nmagic",
"-C", "link-arg=-Tlink.x",
"-C", "link-arg=-Tdefmt.x",
"-C", "inline-threshold=5",
"-C", "no-vectorize-loops",
]

[build]
target = "thumbv6m-none-eabi"
55 changes: 55 additions & 0 deletions hardware_code/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@

[package]
name = "parking_sensor"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"

[[bin]]
name = "parking_sensor"
path = "src/main.rs"

[dependencies]
embassy-executor = { version = "0.7", features = ["task-arena-size-32768", "arch-cortex-m", "executor-thread", "defmt", "integrated-timers"] }
embassy-rp = { version = "0.3", features = ["defmt", "unstable-pac", "time-driver", "critical-section-impl"] }
embassy-time = { version = "0.4", features = ["defmt", "defmt-timestamp-uptime"] }
embassy-sync = { version = "0.6", features = ["defmt"] }
embassy-futures = { version = "0.1" }

# I2C async
embedded-hal-async = "1"
embedded-hal = "1"

# LCD 1602 over I2C (PCF8574 backpack)
ag-lcd = { version = "0.2", default-features = false }
port-expander = "0.7"

defmt = "0.3"
defmt-rtt = "0.4"
panic-probe = { version = "0.3", features = ["print-defmt"] }

cortex-m = { version = "0.7", features = ["inline-asm"] }
cortex-m-rt = "0.7"
static_cell = "2"
fixed = "1"
fixed-macro = "1"

[profile.dev]
debug = 2
debug-assertions = true
opt-level = 1
overflow-checks = true

[profile.release]
debug = 2
debug-assertions = false
opt-level = "s"
overflow-checks = false
lto = "fat"
codegen-units = 1

# cargo build/run
[target.'cfg(all(target_arch = "arm", target_os = "none"))']
runner = "probe-rs run --chip RP2040 --protocol swd"

[build-dependencies]
5 changes: 5 additions & 0 deletions hardware_code/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {

println!("cargo:rerun-if-changed=memory.x");
println!("cargo:rerun-if-changed=build.rs");
}
18 changes: 18 additions & 0 deletions hardware_code/memory.x
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/* Raspberry Pi Pico WH/ RP2040 memory layout */
MEMORY {
/* NOTE: RP2040 boots from external QSPI flash.
The first 256 bytes are reserved for the second-stage bootloader. */
BOOT2 : ORIGIN = 0x10000000, LENGTH = 0x100
FLASH : ORIGIN = 0x10000100, LENGTH = 2048K - 0x100
RAM : ORIGIN = 0x20000000, LENGTH = 256K
}

EXTERN(BOOT2_FIRMWARE)

SECTIONS {
/* Second-stage bootloader – must be the very first section in flash. */
.boot2 ORIGIN(BOOT2) :
{
KEEP(*(.boot2));
} > BOOT2
} INSERT BEFORE .text;
52 changes: 52 additions & 0 deletions hardware_code/src/buzzer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
use embassy_rp::pwm::{Config as PwmConfig, Pwm};
use embassy_time::{Duration, Timer};
use crate::distance::Zone;


const BEEP_FREQ_HZ: u32 = 2_700;


const SYS_CLK_HZ: u32 = 125_000_000;


pub fn make_beep_config() -> PwmConfig {
let wrap = (SYS_CLK_HZ / BEEP_FREQ_HZ).saturating_sub(1) as u16;
let mut cfg = PwmConfig::default();
cfg.top = wrap;
cfg.compare_a = wrap / 2;
cfg
}


pub fn make_silent_config() -> PwmConfig {
let mut cfg = PwmConfig::default();
cfg.top = 0xFFFF;
cfg.compare_a = 0; // 0 % duty → no sound
cfg
}


pub fn set_buzzer_zone(pwm: &mut Pwm<'_>, zone: Zone, beeping_on: bool) {
let cfg = match zone {
Zone::Clear => make_silent_config(),
Zone::Warning => {
if beeping_on {
make_beep_config()
} else {
make_silent_config()
}
}
Zone::Danger => make_beep_config(),
};
pwm.set_config(&cfg);
}

pub async fn half_period(zone: Zone) {
let period_ms = zone.beep_period_ms();
if period_ms > 0 {
Timer::after(Duration::from_millis(period_ms / 2)).await;
} else {

Timer::after(Duration::from_millis(50)).await;
}
}
34 changes: 34 additions & 0 deletions hardware_code/src/distance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

#[derive(Clone, Copy, PartialEq, Eq, defmt::Format)]
pub enum Zone {
Clear,
Warning,
Danger,
}

impl Zone {

pub fn from_sensors(s1_triggered: bool, s2_triggered: bool) -> Self {
match (s1_triggered, s2_triggered) {
(false, false) => Zone::Clear,
(true, false) | (false, true) => Zone::Warning,
(true, true) => Zone::Danger,
}
}

pub fn label(self) -> &'static str {
match self {
Zone::Clear => "CLEAR >100cm ",
Zone::Warning => "WARNING ~50cm ",
Zone::Danger => "DANGER! <20cm ",
}
}

pub fn beep_period_ms(self) -> u64 {
match self {
Zone::Clear => 0,
Zone::Warning => 600, // slow beep
Zone::Danger => 150, // rapid beep
}
}
}
84 changes: 84 additions & 0 deletions hardware_code/src/lcd.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
//lcd.rs

use embassy_rp::i2c::I2c;
use embassy_time::{Duration, Timer};
use embedded_hal_async::i2c::I2c as AsyncI2c;

const BACKLIGHT: u8 = 0b0000_1000;
const ENABLE: u8 = 0b0000_0100;
const RS_DATA: u8 = 0b0000_0001;

pub struct Lcd1602<I2C> {
i2c: I2C,
addr: u8,
}

impl<I2C, E> Lcd1602<I2C>
where
I2C: AsyncI2c<Error = E>,
E: core::fmt::Debug,
{
pub async fn new(i2c: I2C, addr: u8) -> Self {
let mut lcd = Self { i2c, addr };

Timer::after(Duration::from_millis(50)).await;

lcd.write_nibble(0x03, false).await;
Timer::after(Duration::from_millis(5)).await;
lcd.write_nibble(0x03, false).await;
Timer::after(Duration::from_millis(1)).await;
lcd.write_nibble(0x03, false).await;
Timer::after(Duration::from_micros(150)).await;
lcd.write_nibble(0x02, false).await;
lcd.send_cmd(0x28).await;
lcd.send_cmd(0x0C).await;
lcd.send_cmd(0x06).await;
lcd.clear().await;
lcd
}

pub async fn clear(&mut self) {
self.send_cmd(0x01).await;
Timer::after(Duration::from_millis(2)).await;
}

pub async fn set_cursor(&mut self, col: u8, row: u8) {
let row_offset = [0x00u8, 0x40];
let addr = 0x80 | (col + row_offset[row as usize % 2]);
self.send_cmd(addr).await;
}

pub async fn print(&mut self, s: &str) {
for byte in s.bytes() {
self.send_data(byte).await;
}
}



async fn send_cmd(&mut self, cmd: u8) {
self.send_byte(cmd, false).await;
}

async fn send_data(&mut self, data: u8) {
self.send_byte(data, true).await;
}

async fn send_byte(&mut self, byte: u8, rs: bool) {
let hi = (byte >> 4) & 0x0F;
let lo = byte & 0x0F;
self.write_nibble(hi, rs).await;
self.write_nibble(lo, rs).await;
}

async fn write_nibble(&mut self, nibble: u8, rs: bool) {
let rs_bit = if rs { RS_DATA } else { 0 };
let data = (nibble << 4) | BACKLIGHT | rs_bit;


let _ = self.i2c.write(self.addr, &[data | ENABLE]).await;
Timer::after(Duration::from_micros(1)).await;
let _ = self.i2c.write(self.addr, &[data & !ENABLE]).await;
Timer::after(Duration::from_micros(50)).await;
}
}
53 changes: 53 additions & 0 deletions hardware_code/src/led.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//led.rs
use embassy_rp::gpio::{Level, Output};
use crate::distance::Zone;

pub struct RgbLed<'d> {
r: Output<'d>,
g: Output<'d>,
b: Output<'d>,
}

impl<'d> RgbLed<'d> {
pub fn new(r: Output<'d>, g: Output<'d>, b: Output<'d>) -> Self {
let mut led = Self { r, g, b };
led.off();
led
}
pub fn off(&mut self) {
self.r.set_high();
self.g.set_high();
self.b.set_high();
}

pub fn green(&mut self) {
self.r.set_high();
self.g.set_low();
self.b.set_high();
}

pub fn yellow(&mut self) {
self.r.set_low();
self.g.set_low();
self.b.set_high();
}

pub fn red(&mut self) {
self.r.set_low();
self.g.set_high();
self.b.set_high();
}

pub fn set_zone(&mut self, zone: Zone) {
match zone {
Zone::Clear => self.green(),
Zone::Warning => self.yellow(),
Zone::Danger => self.red(),
}
}
}

pub fn set_both_leds(led1: &mut RgbLed<'_>, led2: &mut RgbLed<'_>, zone: Zone) {
led1.set_zone(zone);
led2.set_zone(zone);
}
Loading
Loading