From 833e4d5a56a98ddb833ba85e138dc4ae23f88b32 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Wed, 20 Dec 2023 16:24:36 +0100 Subject: [PATCH] Changed to a cargo workspace Added postcard protocol definitions --- .cargo/config | 19 ------ Cargo.toml | 63 ++--------------- firmware/.cargo/config.toml | 50 ++++++++++++++ firmware/Cargo.toml | 27 ++++++++ {scripts => firmware/scripts}/plot.py | 0 {src => firmware/src}/main.rs | 97 ++++++++++++++++++++------- protocol/Cargo.toml | 9 +++ protocol/src/lib.rs | 21 ++++++ 8 files changed, 186 insertions(+), 100 deletions(-) delete mode 100644 .cargo/config create mode 100644 firmware/.cargo/config.toml create mode 100644 firmware/Cargo.toml rename {scripts => firmware/scripts}/plot.py (100%) rename {src => firmware/src}/main.rs (78%) create mode 100644 protocol/Cargo.toml create mode 100644 protocol/src/lib.rs diff --git a/.cargo/config b/.cargo/config deleted file mode 100644 index bf8dd40..0000000 --- a/.cargo/config +++ /dev/null @@ -1,19 +0,0 @@ -[target.'cfg(all(target_arch = "arm", target_os = "none"))'] -# TODO(2) replace `$CHIP` with your chip's name (see `probe-run --list-chips` output) -runner = "probe-run --chip STM32F103CB" -rustflags = [ - "-C", "linker=flip-link", - "-C", "link-arg=-Tlink.x", - "-C", "link-arg=-Tdefmt.x", - # 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", -] - -[build] -target = "thumbv7m-none-eabi" # Cortex-M3 - - -[alias] -rb = "run --bin" -rrb = "run --release --bin" diff --git a/Cargo.toml b/Cargo.toml index e4f335f..a20d9e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,61 +1,8 @@ -[package] -# TODO(1) fix `authors` and `name` if you didn't use `cargo-generate` -authors = ["sebastian "] -name = "cheapsdp" -edition = "2018" -version = "0.1.0" +[workspace] +resolver = "2" -[dependencies] -cortex-m = { version = "0.7", features = ["critical-section-single-core"] } -defmt = { version = "0.3", features = ["encoding-rzcobs"] } -panic-probe = { version = "0.3", features = ["print-defmt"] } -rtic = { version = "2.0.1", features = [ "thumbv7-backend" ] } -defmt-rtt = "0.4" -embedded-hal = "0.2.3" -stm32f1xx-hal = { version = "0.10.0", features = ["stm32f103", "rt", "medium"] } -nb = "1.0.0" -arrayvec = { version = "0.7.0", default-features = false } -systick-monotonic = "1.0.0" -rtic-monotonics = {version = "1.4.1", features = ["cortex-m-systick"] } -usb-device = "0.2.8" -usbd-serial = "0.1.1" -rtic-sync = {version = "1.1.1"} -ufmt = "0.2.0" +members = [ + "firmware", "protocol", +] -# cargo build/run -[profile.dev] -codegen-units = 1 -debug = 2 -debug-assertions = true # <- -incremental = false -opt-level = 'z' # <- -overflow-checks = true # <- -# cargo test -[profile.test] -codegen-units = 1 -debug = 2 -debug-assertions = true # <- -incremental = false -opt-level = 3 # <- -overflow-checks = true # <- - -# cargo build/run --release -[profile.release] -codegen-units = 1 -debug = 2 -debug-assertions = false # <- -incremental = false -lto = 'fat' -opt-level = 3 # <- -overflow-checks = false # <- - -# cargo test --release -[profile.bench] -codegen-units = 1 -debug = 2 -debug-assertions = false # <- -incremental = false -lto = 'fat' -opt-level = 3 # <- -overflow-checks = false # <- diff --git a/firmware/.cargo/config.toml b/firmware/.cargo/config.toml new file mode 100644 index 0000000..15994fe --- /dev/null +++ b/firmware/.cargo/config.toml @@ -0,0 +1,50 @@ +[target.'cfg(all(target_arch = "arm", target_os = "none"))'] +# TODO(2) replace `$CHIP` with your chip's name (see `probe-run --list-chips` output) +runner = "probe-run --chip STM32F103CB" +rustflags = [ + "-C", "linker=flip-link", + "-C", "link-arg=-Tlink.x", + "-C", "link-arg=-Tdefmt.x", + # 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", +] + + +# cargo build/run +[profile.dev] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 'z' # <- +overflow-checks = true # <- + +# cargo test +[profile.test] +codegen-units = 1 +debug = 2 +debug-assertions = true # <- +incremental = false +opt-level = 3 # <- +overflow-checks = true # <- + +# cargo build/run --release +[profile.release] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- + +# cargo test --release +[profile.bench] +codegen-units = 1 +debug = 2 +debug-assertions = false # <- +incremental = false +lto = 'fat' +opt-level = 3 # <- +overflow-checks = false # <- \ No newline at end of file diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml new file mode 100644 index 0000000..87c70b3 --- /dev/null +++ b/firmware/Cargo.toml @@ -0,0 +1,27 @@ +cargo-features = ["per-package-target"] + +[package] +# TODO(1) fix `authors` and `name` if you didn't use `cargo-generate` +authors = ["sebastian "] +name = "cheapsdo-firmware" +edition = "2021" +version = "0.1.0" +default-target = "thumbv7m-none-eabi" # Cortex-M3 + +[dependencies] +cortex-m = { version = "0.7", features = ["critical-section-single-core"] } +defmt = { version = "0.3", features = ["encoding-rzcobs"] } +defmt-brtt = { version = "0.1", default-features = false, features = ["rtt"] } +panic-probe = { version = "0.3", features = ["print-defmt"] } +rtic = { version = "2.0.1", features = [ "thumbv7-backend" ] } +defmt-rtt = "0.4" +embedded-hal = "0.2.3" +stm32f1xx-hal = { version = "0.10.0", features = ["stm32f103", "rt", "medium"] } +nb = "1.0.0" +systick-monotonic = "1.0.0" +rtic-monotonics = {version = "1.4.1", features = ["cortex-m-systick"] } +usb-device = "0.2.8" +usbd-serial = "0.1.1" +cheapsdo-protocol = { path = "../protocol" } +postcard = {version = "1.0.8", features = ["use-defmt"]} +heapless = {version = "0.8.0", features = ["defmt-03"]} \ No newline at end of file diff --git a/scripts/plot.py b/firmware/scripts/plot.py similarity index 100% rename from scripts/plot.py rename to firmware/scripts/plot.py diff --git a/src/main.rs b/firmware/src/main.rs similarity index 78% rename from src/main.rs rename to firmware/src/main.rs index a296cac..7dce88c 100644 --- a/src/main.rs +++ b/firmware/src/main.rs @@ -1,6 +1,7 @@ #![no_std] #![no_main] #![feature(type_alias_impl_trait)] +#![feature(slice_split_once)] use defmt_rtt as _; // global logger use panic_probe as _; @@ -18,10 +19,7 @@ use rtic::app; #[app(device = stm32f1xx_hal::pac, peripherals = true, dispatchers = [SPI3])] mod app { - use core::fmt::Write; - use cortex_m::asm::delay; - use embedded_hal::digital::v2::OutputPin; use rtic_monotonics::systick::*; use stm32f1xx_hal::{ @@ -37,6 +35,13 @@ mod app { use stm32f1xx_hal::usb::{Peripheral, UsbBus, UsbBusType}; use usb_device::prelude::*; + use heapless::Vec; + use postcard::{from_bytes_cobs, to_vec_cobs}; + + use cheapsdo_protocol::{*, DeviceMessage}; + + const USB_BUFFER_SIZE : usize = 64; + #[local] struct Local { board_led: gpioc::PC13>, @@ -51,6 +56,7 @@ mod app { serial: usbd_serial::SerialPort<'static, UsbBusType>, current_freq: f64, short_avg: f64, + buffer: Vec, } const TARGET_FREQ: f64 = 10.0f64; @@ -193,7 +199,6 @@ mod app { .device_class(usbd_serial::USB_CLASS_CDC) .build(); - update_pwm::spawn().unwrap(); ( @@ -202,6 +207,7 @@ mod app { usb_dev, current_freq: 0.0f64, short_avg: 0.0f64, + buffer: Vec::new(), }, Local { board_led, @@ -212,6 +218,7 @@ mod app { ) } + const WINDOW_LEN: usize = 100; #[task(local=[tim2, tim3, pwm, board_led], shared=[current_freq, short_avg])] @@ -287,7 +294,6 @@ mod app { *avg = short_avg; }); - let diff = (TARGET_FREQ - short_avg) * 1_000_000.0; if diff > 1.0 || diff < -1.0 { cur_pwm += diff as i32 * 15; @@ -307,52 +313,97 @@ mod app { } } - #[task(binds = USB_HP_CAN_TX, shared = [usb_dev, serial, current_freq, short_avg])] + #[task(binds = USB_HP_CAN_TX, shared = [usb_dev, serial, buffer, current_freq, short_avg])] fn usb_tx(cx: usb_tx::Context) { let mut usb_dev = cx.shared.usb_dev; let mut serial = cx.shared.serial; + let mut buffer = cx.shared.buffer; let mut current_freq = cx.shared.current_freq; let mut short_avg = cx.shared.short_avg; - (&mut usb_dev, &mut serial, &mut current_freq, &mut short_avg).lock(|usb_dev, serial, current_freq, short_avg| { - usb_poll(usb_dev, serial, current_freq, short_avg); - }); + ( + &mut usb_dev, + &mut serial, + &mut buffer, + &mut current_freq, + &mut short_avg, + ) + .lock(|usb_dev, serial, buffer, current_freq, short_avg| { + usb_poll(usb_dev, serial, buffer, current_freq, short_avg); + }); } - #[task(binds = USB_LP_CAN_RX0, shared = [usb_dev, serial, current_freq, short_avg])] + #[task(binds = USB_LP_CAN_RX0, shared = [usb_dev, serial, buffer, current_freq, short_avg])] fn usb_rx0(cx: usb_rx0::Context) { let mut usb_dev = cx.shared.usb_dev; let mut serial = cx.shared.serial; + let mut buffer = cx.shared.buffer; let mut current_freq = cx.shared.current_freq; let mut short_avg = cx.shared.short_avg; - (&mut usb_dev, &mut serial, &mut current_freq, &mut short_avg).lock(|usb_dev, serial, current_freq, short_avg| { - usb_poll(usb_dev, serial, current_freq, short_avg); - }); + ( + &mut usb_dev, + &mut serial, + &mut buffer, + &mut current_freq, + &mut short_avg, + ) + .lock(|usb_dev, serial, buffer, current_freq, short_avg| { + usb_poll(usb_dev, serial, buffer, current_freq, short_avg); + }); } fn usb_poll( usb_dev: &mut usb_device::prelude::UsbDevice<'static, B>, serial: &mut usbd_serial::SerialPort<'static, B>, - current_freq: &mut f64, + buffer: &mut Vec, + current_freq: &mut f64, short_avg: &mut f64, ) { if !usb_dev.poll(&mut [serial]) { return; } - let mut buf = [0u8; 8]; - - match serial.read(&mut buf) { + let mut tmp = [0u8; 16]; + match serial.read(&mut tmp) { Ok(count) if count > 0 => { - if buf[0] == b'?' { - - let mut data = arrayvec::ArrayString::<32>::new(); - write!(data, "{}|{}\n\r", current_freq, short_avg).unwrap(); - serial.write(data.as_bytes()).unwrap(); - }; + if buffer.extend_from_slice(&tmp[0..count]).is_err() { + buffer.clear(); + defmt::error!("Buffer overflow while waiting for the end of the packet"); + } } _ => {} } + + loop { + if let Some((msg, rest)) = buffer.split_once(|&x| x == 0) { + let mut message = [0u8; 128]; + message.clone_from_slice(msg); + let host_msg = from_bytes_cobs::(&mut message); + + match host_msg { + Ok(host_msg) => match host_msg { + HostMessage::RequestStatus => { + let device_msg = DeviceMessage::Status(StatusMessage{ + measured_frequency: *current_freq, + average_frequency: *short_avg, + pwm: 0, + }); + + let bytes = to_vec_cobs::(&device_msg).unwrap(); + serial.write(bytes.as_slice()).unwrap(); + }, + HostMessage::SetPLLOutputs => { + defmt::error!("PLL output is not implemented yet") + } + } + Err(err) => defmt::error!("Unable to parse host message: {}", err), + }; + + *buffer = Vec::::from_slice(rest).unwrap(); + } else { + break; + } + } } } diff --git a/protocol/Cargo.toml b/protocol/Cargo.toml new file mode 100644 index 0000000..fe23961 --- /dev/null +++ b/protocol/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "cheapsdo-protocol" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +serde = {version = "1.0.193", default-features = false, features = ["derive"]} diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs new file mode 100644 index 0000000..26b4611 --- /dev/null +++ b/protocol/src/lib.rs @@ -0,0 +1,21 @@ +#![no_std] + +use serde::{Serialize, Deserialize}; + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub enum DeviceMessage { + Status(StatusMessage), +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub enum HostMessage { + RequestStatus, + SetPLLOutputs, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct StatusMessage { + pub measured_frequency: f64, + pub average_frequency: f64, + pub pwm: u16, +}