add configurable Steinhart-Hart equation

This commit is contained in:
Astro 2019-10-02 19:55:33 +02:00
parent 2992688184
commit 8611cc1c79
4 changed files with 132 additions and 21 deletions

View File

@ -50,23 +50,26 @@ The scope of this setting is per TCP session.
### Commands ### Commands
| Syntax | Function | | Syntax | Function |
| --- | --- | | --- | --- |
| `report` | Show current input | | `report` | Show current input |
| `report mode` | Show current report mode | | `report mode` | Show current report mode |
| `report mode <off/on>` | Set report mode | | `report mode <off/on>` | Set report mode |
| `pwm <0/1> max_i_pos <width> <total>` | Set PWM duty cycle for **max_i_pos** to *width / total* | | `pwm <0/1> max_i_pos <width> <total>` | Set PWM duty cycle for **max_i_pos** to *width / total* |
| `pwm <0/1> max_i_neg <width> <total>` | Set PWM duty cycle for **max_i_neg** to *width / total* | | `pwm <0/1> max_i_neg <width> <total>` | Set PWM duty cycle for **max_i_neg** to *width / total* |
| `pwm <0/1> max_v <width> <total>` | Set PWM duty cycle for **max_v** to *width / total* | | `pwm <0/1> max_v <width> <total>` | Set PWM duty cycle for **max_v** to *width / total* |
| `pwm <0/1> <width> <total>` | Set PWM duty cycle for **i_set** to manual *width / total* | | `pwm <0/1> <width> <total>` | Set PWM duty cycle for **i_set** to manual *width / total* |
| `pwm <0/1> pid` | Set PWM to be controlled by PID | | `pwm <0/1> pid` | Set PWM to be controlled by PID |
| `pid` | Show PID configuration | | `pid` | Show PID configuration |
| `pid <0/1> target <value>` | Set the PID controller target | | `pid <0/1> target <value>` | Set the PID controller target |
| `pid <0/1> kp <value>` | Set proportional gain | | `pid <0/1> kp <value>` | Set proportional gain |
| `pid <0/1> ki <value>` | Set integral gain | | `pid <0/1> ki <value>` | Set integral gain |
| `pid <0/1> kd <value>` | Set differential gain | | `pid <0/1> kd <value>` | Set differential gain |
| `pid <0/1> output_min <value>` | Set mininum output | | `pid <0/1> output_min <value>` | Set mininum output |
| `pid <0/1> output_max <value>` | Set maximum output | | `pid <0/1> output_max <value>` | Set maximum output |
| `pid <0/1> integral_min <value>` | Set integral lower bound | | `pid <0/1> integral_min <value>` | Set integral lower bound |
| `pid <0/1> integral_max <value>` | Set integral upper bound | | `pid <0/1> integral_max <value>` | Set integral upper bound |
| `postfilter <0/1> rate <rate>` | Set postfilter output data rate | | `s-h` | Show Steinhart-Hart equation parameters |
| `s-h <0/1> <a/b/c> <value>` | Set Steinhart-Hart parameter for a channel |
| `s-h <0/1> <parallel_resistance> <value>` | Set parallel resistance of the ADC |
| `postfilter <0/1> rate <rate>` | Set postfilter output data rate |

View File

@ -66,6 +66,7 @@ pub enum ShowCommand {
Reporting, Reporting,
Pwm, Pwm,
Pid, Pid,
SteinhartHart,
PostFilter, PostFilter,
} }
@ -81,6 +82,15 @@ pub enum PidParameter {
IntegralMax, IntegralMax,
} }
/// Steinhart-Hart equation parameter
#[derive(Debug, Clone, PartialEq)]
pub enum ShParameter {
A,
B,
C,
ParallelR,
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct PwmConfig { pub struct PwmConfig {
pub width: u16, pub width: u16,
@ -115,6 +125,11 @@ pub enum Command {
parameter: PidParameter, parameter: PidParameter,
value: f32, value: f32,
}, },
SteinhartHart {
channel: usize,
parameter: ShParameter,
value: f32,
},
PostFilter { PostFilter {
channel: usize, channel: usize,
rate: f32, rate: f32,
@ -288,7 +303,7 @@ fn pid_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
Ok((input, result)) Ok((input, result))
} }
/// `pid` | pid_parameter /// `pid` | `pid <pid_parameter>`
fn pid(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> { fn pid(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("pid")(input)?; let (input, _) = tag("pid")(input)?;
alt(( alt((
@ -300,6 +315,35 @@ fn pid(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
))(input) ))(input)
} }
/// `s-h <0-1> <parameter> <value>`
fn steinhart_hart_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
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 <steinhart_hart_parameter>`
fn steinhart_hart(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
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<Command, Error>> { fn postfilter(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
let (input, _) = tag("postfilter")(input)?; let (input, _) = tag("postfilter")(input)?;
alt(( alt((
@ -327,6 +371,7 @@ fn command(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
map(report, Ok), map(report, Ok),
pwm, pwm,
pid, pid,
steinhart_hart,
postfilter, postfilter,
))(input) ))(input)
} }

View File

@ -48,6 +48,8 @@ mod ad7172;
mod pid; mod pid;
mod tec; mod tec;
use tec::{Tec, TecPin}; use tec::{Tec, TecPin};
mod steinhart_hart;
use steinhart_hart as sh;
pub struct UART0; pub struct UART0;
@ -82,6 +84,8 @@ macro_rules! create_socket {
) )
} }
const VCC: f32 = 3.3;
const DEFAULT_PID_PARAMETERS: pid::Parameters = pid::Parameters { const DEFAULT_PID_PARAMETERS: pid::Parameters = pid::Parameters {
kp: 1.0, kp: 1.0,
ki: 1.0, ki: 1.0,
@ -92,6 +96,13 @@ const DEFAULT_PID_PARAMETERS: pid::Parameters = pid::Parameters {
integral_max: 0xffff as f32, 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; const PWM_PID_WIDTH: u16 = 0xffff;
// TODO: maybe rename to `TECS`? // TODO: maybe rename to `TECS`?
@ -106,6 +117,7 @@ struct ControlState {
report: Option<(u64, u32)>, report: Option<(u64, u32)>,
pid_enabled: bool, pid_enabled: bool,
pid: pid::Controller, pid: pid::Controller,
sh: sh::Parameters,
} }
#[cfg(not(test))] #[cfg(not(test))]
@ -183,6 +195,7 @@ fn main() -> ! {
// Start with disengaged PID to let user setup parameters first // Start with disengaged PID to let user setup parameters first
pid_enabled: false, pid_enabled: false,
pid: pid::Controller::new(DEFAULT_PID_PARAMETERS.clone()), pid: pid::Controller::new(DEFAULT_PID_PARAMETERS.clone()),
sh: DEFAULT_SH_PARAMETERS.clone(),
}; };
let mut states = [init_state.clone(), init_state.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) => { Command::Show(ShowCommand::PostFilter) => {
for (channel, _) in states.iter().enumerate() { for (channel, _) in states.iter().enumerate() {
match adc.get_postfilter(channel as u8).unwrap() { match adc.get_postfilter(channel as u8).unwrap() {
@ -411,6 +436,16 @@ fn main() -> ! {
} }
let _ = writeln!(socket, "PID parameter updated"); 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 } => { Command::PostFilter { channel, rate } => {
let filter = ad7172::PostFilter::closest(rate); let filter = ad7172::PostFilter::closest(rate);
match filter { match filter {

View File

@ -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
}
}