diff --git a/Pedestal/firmware/Cargo.lock b/Pedestal/firmware/Cargo.lock index 84b9dff..c97ec9e 100644 --- a/Pedestal/firmware/Cargo.lock +++ b/Pedestal/firmware/Cargo.lock @@ -53,6 +53,26 @@ dependencies = [ "vcell", ] +[[package]] +name = "bytemuck" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.55", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -123,6 +143,7 @@ dependencies = [ name = "firmware" version = "0.1.0" dependencies = [ + "bytemuck", "cortex-m", "cortex-m-rt", "embedded-hal", diff --git a/Pedestal/firmware/Cargo.toml b/Pedestal/firmware/Cargo.toml index caa1db3..95c6bbb 100644 --- a/Pedestal/firmware/Cargo.toml +++ b/Pedestal/firmware/Cargo.toml @@ -20,6 +20,7 @@ panic-semihosting = "0.6.0" usbd-human-interface-device = "0.4.5" packed_struct = { version = "0.10.1", default-features = false } fugit = "0.3.7" +bytemuck = { version = "1.15.0", features = ["derive"] } [dependencies.stm32f1xx-hal] version = "0.10.0" diff --git a/Pedestal/firmware/src/device.rs b/Pedestal/firmware/src/device.rs index 990fac7..d45d871 100644 --- a/Pedestal/firmware/src/device.rs +++ b/Pedestal/firmware/src/device.rs @@ -64,7 +64,7 @@ pub struct CustomInputReport { pub struct CustomOutputReport { #[packed_field] pub integ_lt: u16, - pub test: u16, + pub generic: u16, } pub struct CustomDevice<'a, B: UsbBus> { diff --git a/Pedestal/firmware/src/main.rs b/Pedestal/firmware/src/main.rs index 976278e..f0b816e 100644 --- a/Pedestal/firmware/src/main.rs +++ b/Pedestal/firmware/src/main.rs @@ -2,6 +2,12 @@ #![no_main] #![no_std] +// Silence certain clippy warnings +#![allow(non_upper_case_globals)] +#![allow(clippy::needless_late_init)] +//#![allow()] +//#![allow()] + mod device; use panic_halt as _; @@ -14,29 +20,63 @@ use stm32f1xx_hal::{ pac, prelude::*, timer::{Channel, Tim2NoRemap}, + flash::{FlashWriter, FLASH_START}, + usb::{Peripheral, UsbBus}, }; -use stm32f1xx_hal::usb::{Peripheral, UsbBus}; use usb_device::prelude::*; use usbd_human_interface_device::prelude::*; -use crate::device::{CustomConfig, CustomInputReport, CustomOutputReport}; -use core::cell::OnceCell; +use crate::device::{CustomConfig, CustomInputReport}; +use bytemuck::{bytes_of, try_from_bytes, Pod, Zeroable}; + +// Set layout version +const FLASH_LAYOUT_VERSION: u16 = 0; struct MyPins { pa1: Pin<'A', 1, Analog>, pa2: Pin<'A', 2, Analog>, } +#[derive(Pod)] +#[repr(C)] +#[derive(Copy)] +#[derive(Clone)] +#[derive(Zeroable)] struct CalibrationData { min: u16, max: u16, - factor: OnceCell, + factor: f32, } +impl CalibrationData { + const ADC_MAX: u16 = 4095; + const _dummy: () = { + let size = core::mem::size_of::(); + assert!(size <= 1021, "CalibrationData too big for flash size!"); + }; + fn new (min: u16, max: u16) -> CalibrationData { + return CalibrationData {min, max, factor: calculate_factor(min, max)}; + } +} + +#[derive(Pod)] +#[repr(C)] +#[derive(Copy)] +#[derive(Clone)] +#[derive(Zeroable)] struct Calibration { integ_lt: CalibrationData, - flood_lt: CalibrationData, +// flood_lt: CalibrationData, +} + +impl Calibration { + fn new () -> Calibration { + return Calibration { + integ_lt: CalibrationData::new(0, CalibrationData::ADC_MAX), +// flood_lt: CalibrationData::new(0, CalibrationData::ADC_MAX), + }; + } } @@ -88,10 +128,7 @@ fn main() -> ! { let mut adc1 = adc::Adc::adc1(p.ADC1, clocks); // ====================== Calibration =============== - let cal = Calibration { - integ_lt: CalibrationData {min: 100, max: 4000, factor: OnceCell::new()}, - flood_lt: CalibrationData {min: 0, max: 0, factor: OnceCell::new()}, - }; + let mut cal = load_calibration(); // ====================== Pin setup ================= let mut input_pins = MyPins { @@ -108,7 +145,11 @@ fn main() -> ! { .TIM2 .pwm_hz::(c1, &mut afio.mapr, 1.kHz(), &clocks); pwm.enable(Channel::C1); - let pwm_max = pwm.get_max_duty() as u16; //48000 in our case + let pwm_max = pwm.get_max_duty(); //48000 in our case + + // ====================== Calibration things ======== + let mut calibration_active = false; + let mut calibration_min_done = false; // ====================== Main loop ================= loop { @@ -129,6 +170,7 @@ fn main() -> ! { match consumer.device().read_report() { Err(UsbHidError::WouldBlock) => {} Ok(output) => { + // Set backlight brightness let pwm_val: u16; if output.integ_lt > pwm_max { pwm_val = pwm_max; @@ -137,6 +179,33 @@ fn main() -> ! { pwm_val = output.integ_lt; } pwm.set_duty(Channel::C1, pwm_val); + + // Check generic input field + // Calibration bit + if output.generic & 0x1 == 0x1 { + calibration_active = true; + if !calibration_min_done && output.generic & 0x2 == 0x2 { + cal.integ_lt.min = adc1.read(&mut input_pins.pa1).unwrap(); + calibration_min_done = true; + } + } + else { + if calibration_active { + let max = adc1.read(&mut input_pins.pa1).unwrap(); + if max > cal.integ_lt.min { + cal.integ_lt.max = max; + } + else { + cal.integ_lt.max = CalibrationData::ADC_MAX; + } + cal.integ_lt.factor = calculate_factor(cal.integ_lt.min, cal.integ_lt.max); + // TODO write calibration data +// save_calibration(&mut flash, &cal); + pwm.set_duty(Channel::C1, 10 * pwm_max); + } + calibration_active = false; + calibration_min_done = false; + } } Err(e) => { core::panic!("Failed to write consumer report: {:?}", e) @@ -147,6 +216,11 @@ fn main() -> ! { } } +// Calculate factor from min and max +fn calculate_factor(min: u16, max: u16) -> f32 { + return (CalibrationData::ADC_MAX as f32 / (CalibrationData::ADC_MAX - min - (CalibrationData::ADC_MAX - max)) as f32); +} + // Returns a CustomInputReport from the inputs given fn get_report(pins: &mut MyPins, adc1: &mut adc::Adc, cal: &Calibration) -> CustomInputReport { let integ_lt: u16 = adc1.read(&mut pins.pa1).unwrap(); @@ -158,23 +232,42 @@ fn get_report(pins: &mut MyPins, adc1: &mut adc::Adc, cal: &Calibrati integ_lt_norm = 0; } else if integ_lt > cal.integ_lt.max { - integ_lt_norm = 4095; + integ_lt_norm = CalibrationData::ADC_MAX; } else { - let factor: f32 = *cal.integ_lt.factor.get_or_init(|| cal.integ_lt.get_scale_factor()); - integ_lt_norm = ((integ_lt - cal.integ_lt.min) as f32 * factor) as u16; + integ_lt_norm = ((integ_lt - cal.integ_lt.min) as f32 * cal.integ_lt.factor) as u16; } CustomInputReport { - x: integ_lt_norm.into(), - y: flood_lt.into(), + x: integ_lt_norm, + y: flood_lt, buttons, } } -impl CalibrationData { - const ADC_MAX: u16 = 4095; - fn get_scale_factor (&self) -> f32 { - return (Self::ADC_MAX / (Self::ADC_MAX - self.min - (Self::ADC_MAX - self.max))) as f32; - } +// Save calibration to flash +fn save_calibration(flash: &mut FlashWriter, cal: &Calibration) -> bool { + let mut data: [u8; 1024] = [0; 1024]; + + let encoded_layout_version = bytes_of(&FLASH_LAYOUT_VERSION); + data[0..2].copy_from_slice(encoded_layout_version); + + data[2] = 1; // Calibration available bit + + let encoded_calibration_data = bytes_of(cal); + data[3..][..encoded_calibration_data.len()].copy_from_slice(encoded_calibration_data); + + flash.erase(FLASH_START + 64512, 1024).unwrap(); + match flash.write(FLASH_START + 64512, &data) { + Ok(_ret) => return true, + Err(_e) => return false, + }; +} + +// Load calibration to flash +fn load_calibration() -> Calibration { + let mut cal = Calibration::new(); + // TODO reject loading if version is off or no calibration data found + // TODO return calibration data + return cal; }