Started writing firmware
This commit is contained in:
3
firmware/.gitignore
vendored
Normal file
3
firmware/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/target
|
||||||
|
.idea
|
||||||
|
*.iml
|
||||||
7
firmware/Cargo.lock
generated
Normal file
7
firmware/Cargo.lock
generated
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "film-scanner-fw"
|
||||||
|
version = "0.1.0"
|
||||||
8
firmware/Cargo.toml
Normal file
8
firmware/Cargo.toml
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
[package]
|
||||||
|
name = "film-scanner-fw"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
7
firmware/src/main.rs
Normal file
7
firmware/src/main.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod motion {
|
||||||
|
pub mod planner;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
||||||
139
firmware/src/motion/planner.rs
Normal file
139
firmware/src/motion/planner.rs
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
pub struct Config {
|
||||||
|
j_max: f32, // in mm/s^3
|
||||||
|
v_max: f32, // mm/s^2
|
||||||
|
// mm/s
|
||||||
|
a_max: f32,
|
||||||
|
step_size: f32, // um !
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Planner {
|
||||||
|
tick_frequency: u32,
|
||||||
|
// all measurements are in jd/tick^n, where jd is the distance that makes j_max=6 and n is
|
||||||
|
// the appropriate power of time for the unit.
|
||||||
|
v_max: u32,
|
||||||
|
a_max: u32,
|
||||||
|
step_size: u32,
|
||||||
|
|
||||||
|
// nsteps for each regime
|
||||||
|
xmax_cj: u32,
|
||||||
|
xmax_ca: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Profile {
|
||||||
|
segments: [Segment; 7]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct Segment {
|
||||||
|
// Used by executor
|
||||||
|
pub delta: [u32; 3],
|
||||||
|
start_time: u32, // 0 to disable; set after completion.
|
||||||
|
// used by planner
|
||||||
|
v0: i32,
|
||||||
|
a0: i32,
|
||||||
|
p0: u32, // 0 if the step hasn't been started.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Segment {
|
||||||
|
pub fn state_at_time(&self, time: u32) -> State {
|
||||||
|
let j = self.delta[2] as i32;
|
||||||
|
let
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
pub struct State {
|
||||||
|
time: u32,
|
||||||
|
p0: u32,
|
||||||
|
v0: i32,
|
||||||
|
a0: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn segment_for(&self, j: i32) -> Segment {
|
||||||
|
Segment {
|
||||||
|
delta: [
|
||||||
|
(self.a0 / 2) as u32 + (j / 6) as u32 + self.v0 as u32,
|
||||||
|
self.a0 as u32 + j as u32,
|
||||||
|
j as u32,
|
||||||
|
],
|
||||||
|
start_time: self.time,
|
||||||
|
v0: self.v0,
|
||||||
|
a0: self.a0,
|
||||||
|
p0: self.p0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn produce_segment(&mut self, j: i32, length: u32) -> Segment {
|
||||||
|
let segment = self.segment_for(j);
|
||||||
|
let t = length;
|
||||||
|
let t2 = t * length;
|
||||||
|
let t3 = t2 * length;
|
||||||
|
self.p0 += (j / 6) as u32 * t3 + (self.a0 / 2) as u32 * t2 + self.v0 as u32 * t;
|
||||||
|
self.v0 += j / 2 * t2 as i32 + self.a0 * t as i32;
|
||||||
|
self.a0 += j * t as i32;
|
||||||
|
|
||||||
|
self.time += length as u32;
|
||||||
|
|
||||||
|
segment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Planner {
|
||||||
|
// Note that this uses the `recconfigure` method to apply the configuration, and thus will
|
||||||
|
// clamp v_max such that
|
||||||
|
pub fn new(tick_frequency: u32, config: Config) -> Option<Self> {
|
||||||
|
let mut ret = Self {
|
||||||
|
tick_frequency,
|
||||||
|
v_max: 0,
|
||||||
|
a_max: 0,
|
||||||
|
step_size: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
if ret.reconfigure(config) {
|
||||||
|
Some(ret)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reconfigure the planner to use the given speeds. Note that this should only be run while
|
||||||
|
/// stopped.
|
||||||
|
pub fn reconfigure(&mut self, config: Config) -> bool {
|
||||||
|
let tick_rate = self.tick_frequency as f32;
|
||||||
|
let a_max = (6. * config.a_max * tick_rate / config.j_max)
|
||||||
|
.clamp(0.0, (1 << 32) as f32);
|
||||||
|
let v_max = (6. * config.v_max * tick_rate * tick_rate / config.j_max)
|
||||||
|
.clamp(0.0, (1 << 31) as f32);
|
||||||
|
let step_size = config.step_size * 6. * tick_rate * tick_rate * tick_rate / config.j_max / 1000.;
|
||||||
|
if step_size > (u32::MAX / 2) as f32 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
self.a_max = a_max as u32;
|
||||||
|
self.v_max = v_max as u32;
|
||||||
|
self.step_size = step_size as u32;
|
||||||
|
|
||||||
|
// Compute regime change points
|
||||||
|
let amax2 = self.a_max * self.a_max;
|
||||||
|
self.xmax_cj = self.a_max * amax2 / 18; // jmax = 6, xmax_cj = 2*amax^3/jmax^2
|
||||||
|
self.xmax_ca = self.a_max * self.v_max / 6 + self.v_max * self.v_max / self.a_max;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn plan_profile(&self, dx: i32, state: State) -> Profile {
|
||||||
|
let mut cstate = state;
|
||||||
|
|
||||||
|
let (tj, ta, tv) =
|
||||||
|
if dx.abs() as u32 <= self.xmax_cj {
|
||||||
|
|
||||||
|
};
|
||||||
|
Profile {
|
||||||
|
segments: [
|
||||||
|
Planner::segmentFor()
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user