diff --git a/Cargo.lock b/Cargo.lock index b8099aa..3a5c99c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -146,7 +146,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" dependencies = [ - "rustc_version", + "rustc_version 0.2.3", ] [[package]] @@ -197,6 +197,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cobs" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" + [[package]] name = "colored" version = "1.9.4" @@ -361,6 +367,12 @@ dependencies = [ "nb 1.1.0", ] +[[package]] +name = "embedded-io" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" + [[package]] name = "embedded-storage" version = "0.3.1" @@ -488,6 +500,15 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + [[package]] name = "hash32" version = "0.3.1" @@ -503,13 +524,28 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "heapless" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f" +dependencies = [ + "atomic-polyfill", + "hash32 0.2.1", + "rustc_version 0.4.1", + "serde", + "spin", + "stable_deref_trait", +] + [[package]] name = "heapless" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ - "hash32", + "defmt", + "hash32 0.3.1", "serde", "stable_deref_trait", ] @@ -941,6 +977,18 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" +[[package]] +name = "postcard" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7f0a8d620d71c457dd1d47df76bb18960378da56af4527aaa10f515eee732e" +dependencies = [ + "cobs", + "defmt", + "heapless 0.7.17", + "serde", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -1008,15 +1056,20 @@ dependencies = [ "defmt-brtt", "defmt-rtt", "embedded-hal 1.0.0", + "heapless 0.8.0", "nb 1.1.0", "num", "num-traits", "panic-probe", + "postcard", "qmc5883l", + "radomctl-protocol", "rtic", "rtic-monotonics", "stm32f4xx-hal", "ufmt", + "usb-device", + "usbd-serial", "xca9548a", ] @@ -1024,7 +1077,7 @@ dependencies = [ name = "radomctl-protocol" version = "0.1.0" dependencies = [ - "heapless", + "heapless 0.8.0", "serde", ] @@ -1142,7 +1195,16 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" dependencies = [ - "semver", + "semver 0.9.0", +] + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver 1.0.23", ] [[package]] @@ -1172,6 +1234,12 @@ dependencies = [ "semver-parser", ] +[[package]] +name = "semver" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" + [[package]] name = "semver-parser" version = "0.7.0" @@ -1265,6 +1333,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -1304,6 +1381,7 @@ dependencies = [ "nb 1.1.0", "rand_core", "stm32f4", + "synopsys-usb-otg", "time", "vcell", "void", @@ -1343,6 +1421,18 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +[[package]] +name = "synopsys-usb-otg" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e948d523b316939545d8b21a48c27aef150ce25321b9f95ff7978647a806a6fe" +dependencies = [ + "cortex-m", + "embedded-hal 0.2.7", + "usb-device", + "vcell", +] + [[package]] name = "thiserror" version = "1.0.63" @@ -1597,6 +1687,28 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "usb-device" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98816b1accafbb09085168b90f27e93d790b4bfa19d883466b5e53315b5f06a6" +dependencies = [ + "heapless 0.8.0", + "portable-atomic", +] + +[[package]] +name = "usbd-serial" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065e4eaf93db81d5adac82d9cef8f8da314cb640fa7f89534b972383f1cf80fc" +dependencies = [ + "embedded-hal 0.2.7", + "embedded-io", + "nb 1.1.0", + "usb-device", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index ff5f101..67eb530 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -10,7 +10,7 @@ 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" -stm32f4xx-hal = { version = "0.21.0", features = ["stm32f401"] } +stm32f4xx-hal = { version = "0.21.0", features = ["stm32f401", "usb_fs"] } embedded-hal = {version = "1.0.0"} nb = "1.0.0" num-traits = { version = "0.2", default-features = false, features = ["libm"] } @@ -20,6 +20,12 @@ qmc5883l = "0.0.1" rtic-monotonics = {version = "2.0.2", features = ["cortex-m-systick"]} xca9548a = "0.2.1" as5048a = { git = "https://github.com/LongHairedHacker/as5048a", rev="b15d716bf47ce4975a6cefebf82006c9b09e8fea"} +usb-device = "0.3.2" +usbd-serial = "0.2.2" +postcard = {version = "1.0.10", features = ["use-defmt"]} +heapless = {version = "0.8.0", features = ["defmt-03"]} +radomctl-protocol = { path = "../protocol" } + [features] # set logging levels here diff --git a/firmware/src/main.rs b/firmware/src/main.rs index ac5a199..210dc03 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -13,24 +13,27 @@ fn panic() -> ! { cortex_m::asm::udf() } - #[rtic::app( device = stm32f4xx_hal::pac, dispatchers = [SPI3] )] mod app { - - use as5048a::AS5048A; - + + use heapless::Vec; + use postcard::{from_bytes_cobs, to_vec_cobs}; use stm32f4xx_hal::{ gpio::{gpioa, gpiob, gpioc, Output, PushPull}, i2c, + otg_fs::{UsbBus, UsbBusType, USB}, pac::{I2C1, SPI1}, prelude::*, spi, }; + use usb_device::class_prelude::UsbBusAllocator; + use usb_device::prelude::*; + use usbd_serial::SerialPort; use num_traits::{Float, FloatConst}; @@ -38,10 +41,14 @@ mod app { use qmc5883l::{self, QMC5883L}; + use radomctl_protocol::*; + use rtic_monotonics::systick::prelude::*; systick_monotonic!(Mono, 1000); + const USB_BUFFER_SIZE: usize = 64; + // Shared resources go here #[shared] struct Shared { @@ -60,13 +67,17 @@ mod app { spi_cs3: gpiob::PB15>, spi1: spi::Spi, - az_enable: gpioa::PA12>, + az_enable: gpioa::PA10>, az_dir: gpioa::PA15>, az_step: gpiob::PB3>, el_enable: gpiob::PB4>, el_dir: gpioa::PA8>, el_step: gpioa::PA9>, + + usb_dev: UsbDevice<'static, UsbBusType>, + usb_serial: SerialPort<'static, UsbBusType>, + usb_buffer: Vec, } #[init] @@ -91,8 +102,6 @@ mod app { defmt::info!("Clock Setup done"); - defmt::info!("Clock Setup done"); - // Acquire the GPIO peripherials let gpioa = cx.device.GPIOA.split(); let gpiob = cx.device.GPIOB.split(); @@ -100,6 +109,40 @@ mod app { let board_led = gpioc.pc13.into_push_pull_output(); + defmt::info!("Basic gpio setup done"); + + static mut EP_MEMORY: [u32; 1024] = [0; 1024]; + static mut USB_BUS: Option> = None; + + let usb = USB::new( + ( + cx.device.OTG_FS_GLOBAL, + cx.device.OTG_FS_DEVICE, + cx.device.OTG_FS_PWRCLK, + ), + (gpioa.pa11, gpioa.pa12), + &clocks, + ); + + unsafe { + USB_BUS.replace(UsbBus::new(usb, &mut EP_MEMORY)); + } + + let usb_serial = usbd_serial::SerialPort::new(unsafe { USB_BUS.as_ref().unwrap() }); + + let usb_dev = UsbDeviceBuilder::new( + unsafe { USB_BUS.as_ref().unwrap() }, + UsbVidPid(0x16c0, 0x27dd), + ) + .device_class(usbd_serial::USB_CLASS_CDC) + .strings(&[StringDescriptors::default() + .manufacturer("Amteurfunk Forschungs Gruppe") + .product("Radom Controler")]) + .unwrap() + .build(); + + defmt::info!("USB Setup done"); + // Todo: Check if internal pullups work here let scl = gpiob.pb6.into_alternate_open_drain(); let sda = gpiob.pb7.into_alternate_open_drain(); @@ -144,7 +187,7 @@ mod app { defmt::info!("SPI Setup done"); - let az_enable = gpioa.pa12.into_push_pull_output(); + let az_enable = gpioa.pa10.into_push_pull_output(); let az_dir = gpioa.pa15.into_push_pull_output(); let az_step = gpiob.pb3.into_push_pull_output(); @@ -175,6 +218,10 @@ mod app { el_enable, el_dir, el_step, + + usb_dev, + usb_serial, + usb_buffer: Vec::new(), }, ) } @@ -306,4 +353,64 @@ mod app { } } } + + #[task(binds=OTG_FS, local=[usb_dev, usb_serial, usb_buffer])] + fn usb_fs(cx: usb_fs::Context) { + let usb_dev = cx.local.usb_dev; + let serial = cx.local.usb_serial; + let buffer = cx.local.usb_buffer; + + if !usb_dev.poll(&mut [serial]) { + return; + } + + let mut tmp = [0u8; 16]; + match serial.read(&mut tmp) { + Ok(count) if count > 0 => { + 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(idx) = buffer.iter().position(|&x| x == 0) { + let (msg, rest) = buffer.split_at(idx + 1); + + let mut message = [0u8; 128]; + message[0..msg.len()].clone_from_slice(msg); + let host_msg = from_bytes_cobs::(&mut message); + + match host_msg { + Ok(host_msg) => match host_msg { + HostMessage::RequestStatus => { + let status = StatusMessage { + position: Position { + az: 42.0, + el: 23.0, + az_endcoder: 0.0, + el_encoder: 0.0, + az_magnetic: 0.0, + el_magnetic: 0.0, + }, + alarms: Vec::new(), + }; + let device_msg = RadomMessage::Status(status); + let bytes = + to_vec_cobs::(&device_msg).unwrap(); + serial.write(bytes.as_slice()).unwrap(); + } + HostMessage::TriggerDFUBootloader => todo!(), + }, + Err(err) => defmt::error!("Unable to parse host message: {}", err), + }; + + *buffer = Vec::::from_slice(rest).unwrap(); + } else { + break; + } + } + } } diff --git a/protocol/src/lib.rs b/protocol/src/lib.rs index 4793873..9ae57a1 100644 --- a/protocol/src/lib.rs +++ b/protocol/src/lib.rs @@ -16,18 +16,18 @@ pub enum HostMessage { #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct StatusMessage { - position: Position, - alarms: Vec, + pub position: Position, + pub alarms: Vec, } #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)] pub struct Position { - az: f32, - el: f32, - az_endcoder: f32, - el_encoder: f32, - az_magnetic: f32, - el_magnetic: f32, + pub az: f32, + pub el: f32, + pub az_endcoder: f32, + pub el_encoder: f32, + pub az_magnetic: f32, + pub el_magnetic: f32, } #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]