Calibration working
Signed-off-by: fly <merspieler@alwaysdata.com>
This commit is contained in:
parent
0f20037433
commit
384226e543
4 changed files with 136 additions and 21 deletions
21
Pedestal/firmware/Cargo.lock
generated
21
Pedestal/firmware/Cargo.lock
generated
|
@ -53,6 +53,26 @@ dependencies = [
|
||||||
"vcell",
|
"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]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -123,6 +143,7 @@ dependencies = [
|
||||||
name = "firmware"
|
name = "firmware"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
|
|
|
@ -20,6 +20,7 @@ panic-semihosting = "0.6.0"
|
||||||
usbd-human-interface-device = "0.4.5"
|
usbd-human-interface-device = "0.4.5"
|
||||||
packed_struct = { version = "0.10.1", default-features = false }
|
packed_struct = { version = "0.10.1", default-features = false }
|
||||||
fugit = "0.3.7"
|
fugit = "0.3.7"
|
||||||
|
bytemuck = { version = "1.15.0", features = ["derive"] }
|
||||||
|
|
||||||
[dependencies.stm32f1xx-hal]
|
[dependencies.stm32f1xx-hal]
|
||||||
version = "0.10.0"
|
version = "0.10.0"
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub struct CustomInputReport {
|
||||||
pub struct CustomOutputReport {
|
pub struct CustomOutputReport {
|
||||||
#[packed_field]
|
#[packed_field]
|
||||||
pub integ_lt: u16,
|
pub integ_lt: u16,
|
||||||
pub test: u16,
|
pub generic: u16,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CustomDevice<'a, B: UsbBus> {
|
pub struct CustomDevice<'a, B: UsbBus> {
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![no_std]
|
#![no_std]
|
||||||
|
|
||||||
|
// Silence certain clippy warnings
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(clippy::needless_late_init)]
|
||||||
|
//#![allow()]
|
||||||
|
//#![allow()]
|
||||||
|
|
||||||
mod device;
|
mod device;
|
||||||
|
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
|
@ -14,29 +20,63 @@ use stm32f1xx_hal::{
|
||||||
pac,
|
pac,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
timer::{Channel, Tim2NoRemap},
|
timer::{Channel, Tim2NoRemap},
|
||||||
|
flash::{FlashWriter, FLASH_START},
|
||||||
|
usb::{Peripheral, UsbBus},
|
||||||
};
|
};
|
||||||
|
|
||||||
use stm32f1xx_hal::usb::{Peripheral, UsbBus};
|
|
||||||
use usb_device::prelude::*;
|
use usb_device::prelude::*;
|
||||||
use usbd_human_interface_device::prelude::*;
|
use usbd_human_interface_device::prelude::*;
|
||||||
|
|
||||||
use crate::device::{CustomConfig, CustomInputReport, CustomOutputReport};
|
use crate::device::{CustomConfig, CustomInputReport};
|
||||||
use core::cell::OnceCell;
|
use bytemuck::{bytes_of, try_from_bytes, Pod, Zeroable};
|
||||||
|
|
||||||
|
// Set layout version
|
||||||
|
const FLASH_LAYOUT_VERSION: u16 = 0;
|
||||||
|
|
||||||
struct MyPins {
|
struct MyPins {
|
||||||
pa1: Pin<'A', 1, Analog>,
|
pa1: Pin<'A', 1, Analog>,
|
||||||
pa2: Pin<'A', 2, Analog>,
|
pa2: Pin<'A', 2, Analog>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Pod)]
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Copy)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
#[derive(Zeroable)]
|
||||||
struct CalibrationData {
|
struct CalibrationData {
|
||||||
min: u16,
|
min: u16,
|
||||||
max: u16,
|
max: u16,
|
||||||
factor: OnceCell<f32>,
|
factor: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CalibrationData {
|
||||||
|
const ADC_MAX: u16 = 4095;
|
||||||
|
const _dummy: () = {
|
||||||
|
let size = core::mem::size_of::<CalibrationData>();
|
||||||
|
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 {
|
struct Calibration {
|
||||||
integ_lt: CalibrationData,
|
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);
|
let mut adc1 = adc::Adc::adc1(p.ADC1, clocks);
|
||||||
|
|
||||||
// ====================== Calibration ===============
|
// ====================== Calibration ===============
|
||||||
let cal = Calibration {
|
let mut cal = load_calibration();
|
||||||
integ_lt: CalibrationData {min: 100, max: 4000, factor: OnceCell::new()},
|
|
||||||
flood_lt: CalibrationData {min: 0, max: 0, factor: OnceCell::new()},
|
|
||||||
};
|
|
||||||
|
|
||||||
// ====================== Pin setup =================
|
// ====================== Pin setup =================
|
||||||
let mut input_pins = MyPins {
|
let mut input_pins = MyPins {
|
||||||
|
@ -108,7 +145,11 @@ fn main() -> ! {
|
||||||
.TIM2
|
.TIM2
|
||||||
.pwm_hz::<Tim2NoRemap, _, _>(c1, &mut afio.mapr, 1.kHz(), &clocks);
|
.pwm_hz::<Tim2NoRemap, _, _>(c1, &mut afio.mapr, 1.kHz(), &clocks);
|
||||||
pwm.enable(Channel::C1);
|
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 =================
|
// ====================== Main loop =================
|
||||||
loop {
|
loop {
|
||||||
|
@ -129,6 +170,7 @@ fn main() -> ! {
|
||||||
match consumer.device().read_report() {
|
match consumer.device().read_report() {
|
||||||
Err(UsbHidError::WouldBlock) => {}
|
Err(UsbHidError::WouldBlock) => {}
|
||||||
Ok(output) => {
|
Ok(output) => {
|
||||||
|
// Set backlight brightness
|
||||||
let pwm_val: u16;
|
let pwm_val: u16;
|
||||||
if output.integ_lt > pwm_max {
|
if output.integ_lt > pwm_max {
|
||||||
pwm_val = pwm_max;
|
pwm_val = pwm_max;
|
||||||
|
@ -137,6 +179,33 @@ fn main() -> ! {
|
||||||
pwm_val = output.integ_lt;
|
pwm_val = output.integ_lt;
|
||||||
}
|
}
|
||||||
pwm.set_duty(Channel::C1, pwm_val);
|
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) => {
|
Err(e) => {
|
||||||
core::panic!("Failed to write consumer report: {:?}", 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
|
// Returns a CustomInputReport from the inputs given
|
||||||
fn get_report(pins: &mut MyPins, adc1: &mut adc::Adc<pac::ADC1>, cal: &Calibration) -> CustomInputReport {
|
fn get_report(pins: &mut MyPins, adc1: &mut adc::Adc<pac::ADC1>, cal: &Calibration) -> CustomInputReport {
|
||||||
let integ_lt: u16 = adc1.read(&mut pins.pa1).unwrap();
|
let integ_lt: u16 = adc1.read(&mut pins.pa1).unwrap();
|
||||||
|
@ -158,23 +232,42 @@ fn get_report(pins: &mut MyPins, adc1: &mut adc::Adc<pac::ADC1>, cal: &Calibrati
|
||||||
integ_lt_norm = 0;
|
integ_lt_norm = 0;
|
||||||
}
|
}
|
||||||
else if integ_lt > cal.integ_lt.max {
|
else if integ_lt > cal.integ_lt.max {
|
||||||
integ_lt_norm = 4095;
|
integ_lt_norm = CalibrationData::ADC_MAX;
|
||||||
}
|
}
|
||||||
else {
|
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 * cal.integ_lt.factor) as u16;
|
||||||
integ_lt_norm = ((integ_lt - cal.integ_lt.min) as f32 * factor) as u16;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomInputReport {
|
CustomInputReport {
|
||||||
x: integ_lt_norm.into(),
|
x: integ_lt_norm,
|
||||||
y: flood_lt.into(),
|
y: flood_lt,
|
||||||
buttons,
|
buttons,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CalibrationData {
|
// Save calibration to flash
|
||||||
const ADC_MAX: u16 = 4095;
|
fn save_calibration(flash: &mut FlashWriter, cal: &Calibration) -> bool {
|
||||||
fn get_scale_factor (&self) -> f32 {
|
let mut data: [u8; 1024] = [0; 1024];
|
||||||
return (Self::ADC_MAX / (Self::ADC_MAX - self.min - (Self::ADC_MAX - self.max))) as f32;
|
|
||||||
}
|
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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue