diff --git a/firmware/README.md b/firmware/README.md index 6e2db54..73b0f1a 100644 --- a/firmware/README.md +++ b/firmware/README.md @@ -50,23 +50,26 @@ The scope of this setting is per TCP session. ### Commands -| Syntax | Function | -| --- | --- | -| `report` | Show current input | -| `report mode` | Show current report mode | -| `report mode ` | Set report mode | -| `pwm <0/1> max_i_pos ` | Set PWM duty cycle for **max_i_pos** to *width / total* | -| `pwm <0/1> max_i_neg ` | Set PWM duty cycle for **max_i_neg** to *width / total* | -| `pwm <0/1> max_v ` | Set PWM duty cycle for **max_v** to *width / total* | -| `pwm <0/1> ` | Set PWM duty cycle for **i_set** to manual *width / total* | -| `pwm <0/1> pid` | Set PWM to be controlled by PID | -| `pid` | Show PID configuration | -| `pid <0/1> target ` | Set the PID controller target | -| `pid <0/1> kp ` | Set proportional gain | -| `pid <0/1> ki ` | Set integral gain | -| `pid <0/1> kd ` | Set differential gain | -| `pid <0/1> output_min ` | Set mininum output | -| `pid <0/1> output_max ` | Set maximum output | -| `pid <0/1> integral_min ` | Set integral lower bound | -| `pid <0/1> integral_max ` | Set integral upper bound | -| `postfilter <0/1> rate ` | Set postfilter output data rate | +| Syntax | Function | +| --- | --- | +| `report` | Show current input | +| `report mode` | Show current report mode | +| `report mode ` | Set report mode | +| `pwm <0/1> max_i_pos ` | Set PWM duty cycle for **max_i_pos** to *width / total* | +| `pwm <0/1> max_i_neg ` | Set PWM duty cycle for **max_i_neg** to *width / total* | +| `pwm <0/1> max_v ` | Set PWM duty cycle for **max_v** to *width / total* | +| `pwm <0/1> ` | Set PWM duty cycle for **i_set** to manual *width / total* | +| `pwm <0/1> pid` | Set PWM to be controlled by PID | +| `pid` | Show PID configuration | +| `pid <0/1> target ` | Set the PID controller target | +| `pid <0/1> kp ` | Set proportional gain | +| `pid <0/1> ki ` | Set integral gain | +| `pid <0/1> kd ` | Set differential gain | +| `pid <0/1> output_min ` | Set mininum output | +| `pid <0/1> output_max ` | Set maximum output | +| `pid <0/1> integral_min ` | Set integral lower bound | +| `pid <0/1> integral_max ` | Set integral upper bound | +| `s-h` | Show Steinhart-Hart equation parameters | +| `s-h <0/1> ` | Set Steinhart-Hart parameter for a channel | +| `s-h <0/1> ` | Set parallel resistance of the ADC | +| `postfilter <0/1> rate ` | Set postfilter output data rate | diff --git a/firmware/src/command_parser.rs b/firmware/src/command_parser.rs index 0514f72..0aab5b8 100644 --- a/firmware/src/command_parser.rs +++ b/firmware/src/command_parser.rs @@ -66,6 +66,7 @@ pub enum ShowCommand { Reporting, Pwm, Pid, + SteinhartHart, PostFilter, } @@ -81,6 +82,15 @@ pub enum PidParameter { IntegralMax, } +/// Steinhart-Hart equation parameter +#[derive(Debug, Clone, PartialEq)] +pub enum ShParameter { + A, + B, + C, + ParallelR, +} + #[derive(Debug, Clone, PartialEq)] pub struct PwmConfig { pub width: u16, @@ -115,6 +125,11 @@ pub enum Command { parameter: PidParameter, value: f32, }, + SteinhartHart { + channel: usize, + parameter: ShParameter, + value: f32, + }, PostFilter { channel: usize, rate: f32, @@ -288,7 +303,7 @@ fn pid_parameter(input: &[u8]) -> IResult<&[u8], Result> { Ok((input, result)) } -/// `pid` | pid_parameter +/// `pid` | `pid ` fn pid(input: &[u8]) -> IResult<&[u8], Result> { let (input, _) = tag("pid")(input)?; alt(( @@ -300,6 +315,35 @@ fn pid(input: &[u8]) -> IResult<&[u8], Result> { ))(input) } +/// `s-h <0-1> ` +fn steinhart_hart_parameter(input: &[u8]) -> IResult<&[u8], Result> { + let (input, channel) = channel(input)?; + let (input, _) = whitespace(input)?; + let (input, parameter) = + alt((value(ShParameter::A, tag("a")), + value(ShParameter::B, tag("b")), + value(ShParameter::C, tag("c")), + value(ShParameter::ParallelR, tag("parallel_resistance")) + ))(input)?; + let (input, _) = whitespace(input)?; + let (input, value) = float(input)?; + let result = value + .map(|value| Command::SteinhartHart { channel, parameter, value }); + Ok((input, result)) +} + +/// `s-h` | `s-h ` +fn steinhart_hart(input: &[u8]) -> IResult<&[u8], Result> { + let (input, _) = tag("s-h")(input)?; + alt(( + preceded( + whitespace, + steinhart_hart_parameter + ), + value(Ok(Command::Show(ShowCommand::SteinhartHart)), end) + ))(input) +} + fn postfilter(input: &[u8]) -> IResult<&[u8], Result> { let (input, _) = tag("postfilter")(input)?; alt(( @@ -327,6 +371,7 @@ fn command(input: &[u8]) -> IResult<&[u8], Result> { map(report, Ok), pwm, pid, + steinhart_hart, postfilter, ))(input) } diff --git a/firmware/src/main.rs b/firmware/src/main.rs index fa67ef9..5b61778 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -48,6 +48,8 @@ mod ad7172; mod pid; mod tec; use tec::{Tec, TecPin}; +mod steinhart_hart; +use steinhart_hart as sh; pub struct UART0; @@ -82,6 +84,8 @@ macro_rules! create_socket { ) } +const VCC: f32 = 3.3; + const DEFAULT_PID_PARAMETERS: pid::Parameters = pid::Parameters { kp: 1.0, ki: 1.0, @@ -92,6 +96,13 @@ const DEFAULT_PID_PARAMETERS: pid::Parameters = pid::Parameters { integral_max: 0xffff as f32, }; +const DEFAULT_SH_PARAMETERS: sh::Parameters = sh::Parameters { + a: 0.01, + b: 0.01, + c: 0.01, + parallel_r: 100.0, // TODO +}; + const PWM_PID_WIDTH: u16 = 0xffff; // TODO: maybe rename to `TECS`? @@ -106,6 +117,7 @@ struct ControlState { report: Option<(u64, u32)>, pid_enabled: bool, pid: pid::Controller, + sh: sh::Parameters, } #[cfg(not(test))] @@ -183,6 +195,7 @@ fn main() -> ! { // Start with disengaged PID to let user setup parameters first pid_enabled: false, pid: pid::Controller::new(DEFAULT_PID_PARAMETERS.clone()), + sh: DEFAULT_SH_PARAMETERS.clone(), }; let mut states = [init_state.clone(), init_state.clone()]; @@ -330,6 +343,18 @@ fn main() -> ! { } } } + Command::Show(ShowCommand::SteinhartHart) => { + for (channel, state) in states.iter().enumerate() { + let _ = writeln!( + socket, "Channel {} parameters for the Steinhart-Hart equation", + channel, + ); + let _ = writeln!(socket, "- a={}", state.sh.a); + let _ = writeln!(socket, "- b={}", state.sh.b); + let _ = writeln!(socket, "- c={}", state.sh.c); + let _ = writeln!(socket, "- parallel_r={}", state.sh.parallel_r); + } + } Command::Show(ShowCommand::PostFilter) => { for (channel, _) in states.iter().enumerate() { match adc.get_postfilter(channel as u8).unwrap() { @@ -411,6 +436,16 @@ fn main() -> ! { } let _ = writeln!(socket, "PID parameter updated"); } + Command::SteinhartHart { channel, parameter, value } => { + let sh = &mut states[channel].sh; + use command_parser::ShParameter::*; + match parameter { + A => sh.a = value, + B => sh.b = value, + C => sh.c = value, + ParallelR => sh.parallel_r = value, + } + } Command::PostFilter { channel, rate } => { let filter = ad7172::PostFilter::closest(rate); match filter { diff --git a/firmware/src/steinhart_hart.rs b/firmware/src/steinhart_hart.rs new file mode 100644 index 0000000..08f2d97 --- /dev/null +++ b/firmware/src/steinhart_hart.rs @@ -0,0 +1,28 @@ +use libm::F32Ext; + +/// Steinhart-Hart equation parameters +#[derive(Clone, Debug)] +pub struct Parameters { + pub a: f32, + pub b: f32, + pub c: f32, + /// Parallel resistance + /// + /// Not truly part of the equation but required to calculate + /// resistance from voltage. + pub parallel_r: f32, +} + +impl Parameters { + /// input: Voltage + /// + /// Result unit: Kelvin + pub fn get_temperature(&self, input: f32) -> f32 { + let r = self.parallel_r * input; + let ln_r = r.ln(); + let inv_temp = self.a + + self.b * ln_r + + self.c * ln_r * ln_r * ln_r; + 1.0 / inv_temp + } +}