forked from M-Labs/ionpak-thermostat
add electrometer, introduce *Status objects
This commit is contained in:
parent
c676102b33
commit
44d95973ca
|
@ -37,6 +37,10 @@ pub const FD_ADC_GAIN: f32 = 3111.1111111111104;
|
|||
pub const FD_ADC_OFFSET: f32 = 96.0;
|
||||
pub const FBV_ADC_GAIN: f32 = 49.13796058269066;
|
||||
pub const FBV_PWM_GAIN: f32 = 0.5730803571428571;
|
||||
pub const IC_ADC_GAIN_LOW: f32 = 1333333333333.3333;
|
||||
pub const IC_ADC_GAIN_MED: f32 = 13201320132.0132;
|
||||
pub const IC_ADC_GAIN_HIGH: f32 = 133320001.3332;
|
||||
pub const IC_ADC_OFFSET: f32 = 96.0;
|
||||
|
||||
pub const FBI_R223: f32 = 200.0;
|
||||
pub const FBI_R224: f32 = 39.0;
|
||||
|
@ -80,7 +84,6 @@ pub fn set_fbv_pwm(duty: u16) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum EmissionRange {
|
||||
Low, // 22K
|
||||
|
@ -102,6 +105,27 @@ pub fn set_emission_range(range: EmissionRange) {
|
|||
});
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ElectrometerRange {
|
||||
Low, // 1G
|
||||
Med, // 1G//10M
|
||||
High // 1G//100K
|
||||
}
|
||||
|
||||
pub fn set_electrometer_range(range: ElectrometerRange) {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
let gpio_p = tm4c129x::GPIO_PORTP.borrow(cs);
|
||||
gpio_p.data.modify(|r, w| {
|
||||
let value = r.data().bits() & 0b111100;
|
||||
match range {
|
||||
ElectrometerRange::Low => w.data().bits(value | 0b000000),
|
||||
ElectrometerRange::Med => w.data().bits(value | 0b000001),
|
||||
ElectrometerRange::High => w.data().bits(value | 0b000010),
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn reset_error() {
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
let gpio_q = tm4c129x::GPIO_PORTQ.borrow(cs);
|
||||
|
@ -201,6 +225,8 @@ pub fn init() {
|
|||
let gpio_p = tm4c129x::GPIO_PORTP.borrow(cs);
|
||||
gpio_p.dir.write(|w| w.dir().bits(0b111111));
|
||||
gpio_p.den.write(|w| w.den().bits(0b111111));
|
||||
set_emission_range(EmissionRange::Med);
|
||||
set_electrometer_range(ElectrometerRange::Med);
|
||||
|
||||
// Set up error pins
|
||||
let gpio_l = tm4c129x::GPIO_PORTL.borrow(cs);
|
||||
|
@ -209,7 +235,7 @@ pub fn init() {
|
|||
gpio_l.den.write(|w| w.den().bits(FV_ERRN|FBV_ERRN|FBI_ERRN|AV_ERRN|AI_ERRN|ERR_LATCHN));
|
||||
gpio_q.dir.write(|w| w.dir().bits(ERR_RESN));
|
||||
gpio_q.den.write(|w| w.den().bits(ERR_RESN));
|
||||
reset_error();
|
||||
reset_error(); // error latch is an undefined state upon power-up; reset it
|
||||
|
||||
// Set up PWMs
|
||||
let gpio_f = tm4c129x::GPIO_PORTF_AHB.borrow(cs);
|
||||
|
|
|
@ -0,0 +1,56 @@
|
|||
use board;
|
||||
|
||||
pub struct Electrometer {
|
||||
range: board::ElectrometerRange,
|
||||
ic_buffer: [f32; 16],
|
||||
ic_buffer_count: usize,
|
||||
last_ic: Option<f32>
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ElectrometerStatus {
|
||||
pub ic: Option<f32>
|
||||
}
|
||||
|
||||
impl Electrometer {
|
||||
pub const fn new() -> Electrometer {
|
||||
Electrometer {
|
||||
range: board::ElectrometerRange::Med,
|
||||
ic_buffer: [0.0; 16],
|
||||
ic_buffer_count: 0,
|
||||
last_ic: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn adc_input(&mut self, ic_sample: u16) {
|
||||
let gain = match self.range {
|
||||
board::ElectrometerRange::Low => board::IC_ADC_GAIN_LOW,
|
||||
board::ElectrometerRange::Med => board::IC_ADC_GAIN_MED,
|
||||
board::ElectrometerRange::High => board::IC_ADC_GAIN_HIGH
|
||||
};
|
||||
self.ic_buffer[self.ic_buffer_count] = ((ic_sample as f32) - board::IC_ADC_OFFSET)/gain;
|
||||
self.ic_buffer_count += 1;
|
||||
if self.ic_buffer_count == self.ic_buffer.len() {
|
||||
let mut ic_avg: f32 = 0.0;
|
||||
for ic in self.ic_buffer.iter() {
|
||||
ic_avg += *ic;
|
||||
}
|
||||
self.last_ic = Some(ic_avg/(self.ic_buffer.len() as f32));
|
||||
self.ic_buffer_count = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_status(&self) -> ElectrometerStatus {
|
||||
ElectrometerStatus {
|
||||
ic: self.last_ic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ElectrometerStatus {
|
||||
pub fn debug_print(&self) {
|
||||
if self.ic.is_some() {
|
||||
println!("ion current: {}nA", 1e9*self.ic.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,11 @@
|
|||
use core::num::Float;
|
||||
|
||||
use board;
|
||||
use pid;
|
||||
|
||||
const PID_PARAMETERS: pid::Parameters = pid::Parameters {
|
||||
kp: 0.004,
|
||||
ki: 0.002,
|
||||
kp: 0.027,
|
||||
ki: 0.013,
|
||||
kd: 0.0,
|
||||
output_min: 0.0,
|
||||
output_max: 30.0,
|
||||
|
@ -13,38 +15,63 @@ const PID_PARAMETERS: pid::Parameters = pid::Parameters {
|
|||
|
||||
|
||||
pub struct Controller {
|
||||
pid: pid::Controller
|
||||
pid: pid::Controller,
|
||||
target: f32,
|
||||
last_av: Option<f32>
|
||||
}
|
||||
|
||||
pub struct ControllerStatus {
|
||||
pub ready: bool,
|
||||
pub av: Option<f32>
|
||||
}
|
||||
|
||||
impl Controller {
|
||||
pub const fn new() -> Controller {
|
||||
Controller {
|
||||
pid: pid::Controller::new(PID_PARAMETERS)
|
||||
pid: pid::Controller::new(PID_PARAMETERS),
|
||||
target: 0.0,
|
||||
last_av: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn adc_input(&mut self, av_sample: u16) {
|
||||
let pid_out = self.pid.update(av_sample as f32);
|
||||
let av = av_sample as f32/board::AV_ADC_GAIN;
|
||||
self.last_av = Some(av);
|
||||
|
||||
let pid_out = self.pid.update(av);
|
||||
board::set_hv_pwm(pid_out as u16)
|
||||
}
|
||||
|
||||
pub fn set_target(&mut self, volts: f32) {
|
||||
self.pid.target = volts*board::AV_ADC_GAIN;
|
||||
self.target = 0.0;
|
||||
self.pid.set_target(volts);
|
||||
}
|
||||
|
||||
pub fn ready(&self) -> bool {
|
||||
self.pid.is_within(1.0*board::AV_ADC_GAIN)
|
||||
fn ready(&self) -> bool {
|
||||
match self.last_av {
|
||||
None => false,
|
||||
Some(last_av) => (last_av - self.target).abs() < 1.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.pid.reset();
|
||||
board::set_hv_pwm(0);
|
||||
}
|
||||
|
||||
pub fn get_status(&self) -> ControllerStatus {
|
||||
ControllerStatus {
|
||||
ready: self.ready(),
|
||||
av: self.last_av
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ControllerStatus {
|
||||
pub fn debug_print(&self) {
|
||||
println!("anode ready: {}", self.ready());
|
||||
if self.pid.last_input.is_some() {
|
||||
println!("voltage: {}V", self.pid.last_input.unwrap()/board::AV_ADC_GAIN);
|
||||
println!("anode ready: {}", self.ready);
|
||||
if self.av.is_some() {
|
||||
println!("voltage: {}V", self.av.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,12 +27,20 @@ pub struct Controller {
|
|||
last_fbv: Option<f32>
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ControllerStatus {
|
||||
pub ready: bool,
|
||||
pub fbi: Option<f32>,
|
||||
pub fv: Option<f32>,
|
||||
pub fbv: Option<f32>
|
||||
}
|
||||
|
||||
|
||||
impl Controller {
|
||||
pub const fn new() -> Controller {
|
||||
Controller {
|
||||
fbi_target: 0.0,
|
||||
fbi_range: board::EmissionRange::Low,
|
||||
fbi_range: board::EmissionRange::Med,
|
||||
fbi_buffer: [0.0; 16],
|
||||
fbi_buffer_count: 0,
|
||||
last_fbi: None,
|
||||
|
@ -91,25 +99,20 @@ impl Controller {
|
|||
board::set_fbv_pwm((volts/board::FBV_PWM_GAIN) as u16);
|
||||
}
|
||||
|
||||
pub fn emission_ready(&self) -> bool {
|
||||
let tolerance = if self.fbi_range == board::EmissionRange::High { 0.20 } else { 0.02 };
|
||||
fn emission_ready(&self) -> bool {
|
||||
match self.last_fbi {
|
||||
None => false,
|
||||
Some(last_fbi) => (self.fbi_target - last_fbi).abs()/self.fbi_target < tolerance
|
||||
Some(last_fbi) => (self.fbi_target - last_fbi).abs()/self.fbi_target < 0.02
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bias_ready(&self) -> bool {
|
||||
fn bias_ready(&self) -> bool {
|
||||
match self.last_fbv {
|
||||
None => false,
|
||||
Some(last_fbv) => (self.fbv_target - last_fbv).abs() < 1.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ready(&self) -> bool {
|
||||
self.emission_ready() & self.bias_ready()
|
||||
}
|
||||
|
||||
pub fn reset(&mut self) {
|
||||
self.pid.reset();
|
||||
self.fbi_buffer_count = 0;
|
||||
|
@ -118,16 +121,28 @@ impl Controller {
|
|||
self.last_fbv = None;
|
||||
}
|
||||
|
||||
pub fn get_status(&self) -> ControllerStatus {
|
||||
ControllerStatus {
|
||||
ready: self.emission_ready() & self.bias_ready(),
|
||||
fbi: self.last_fbi,
|
||||
fv: self.last_fv,
|
||||
fbv: self.last_fbv
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl ControllerStatus {
|
||||
pub fn debug_print(&self) {
|
||||
println!("cathode ready: {}", self.ready());
|
||||
if self.last_fbi.is_some() {
|
||||
println!("emission: {}mA", 1000.0*self.last_fbi.unwrap());
|
||||
println!("cathode ready: {}", self.ready);
|
||||
if self.fbi.is_some() {
|
||||
println!("emission: {}mA", 1000.0*self.fbi.unwrap());
|
||||
}
|
||||
if self.last_fv.is_some() {
|
||||
println!("fil voltage: {}V", self.last_fv.unwrap());
|
||||
if self.fv.is_some() {
|
||||
println!("fil voltage: {}V", self.fv.unwrap());
|
||||
}
|
||||
if self.last_fbv.is_some() {
|
||||
println!("bias voltage: {}V", self.last_fbv.unwrap());
|
||||
if self.fbv.is_some() {
|
||||
println!("bias voltage: {}V", self.fbv.unwrap());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ mod board;
|
|||
mod pid;
|
||||
mod loop_anode;
|
||||
mod loop_cathode;
|
||||
mod electrometer;
|
||||
|
||||
static TIME: Mutex<Cell<u64>> = Mutex::new(Cell::new(0));
|
||||
|
||||
|
@ -46,6 +47,9 @@ static LOOP_ANODE: Mutex<RefCell<loop_anode::Controller>> = Mutex::new(RefCell::
|
|||
static LOOP_CATHODE: Mutex<RefCell<loop_cathode::Controller>> = Mutex::new(RefCell::new(
|
||||
loop_cathode::Controller::new()));
|
||||
|
||||
static ELECTROMETER: Mutex<RefCell<electrometer::Electrometer>> = Mutex::new(RefCell::new(
|
||||
electrometer::Electrometer::new()));
|
||||
|
||||
|
||||
pub struct UART0;
|
||||
|
||||
|
@ -80,7 +84,7 @@ fn main() {
|
|||
loop_anode.set_target(anode_cathode+cathode_bias);
|
||||
loop_cathode.set_emission_target(anode_cathode/10000.0);
|
||||
loop_cathode.set_bias_target(cathode_bias);
|
||||
board::set_fv_pwm(10);
|
||||
//board::set_fv_pwm(10);
|
||||
});
|
||||
|
||||
println!(r#"
|
||||
|
@ -126,12 +130,12 @@ Ready."#);
|
|||
Some(t) => if time > t {
|
||||
latch_reset_time = None;
|
||||
cortex_m::interrupt::free(|cs| {
|
||||
board::reset_error();
|
||||
// reset PID loops as they have accumulated large errors
|
||||
// while the protection was active, which would cause
|
||||
// unnecessary overshoots.
|
||||
LOOP_ANODE.borrow(cs).borrow_mut().reset();
|
||||
LOOP_CATHODE.borrow(cs).borrow_mut().reset();
|
||||
board::reset_error();
|
||||
});
|
||||
println!("Protection reset");
|
||||
}
|
||||
|
@ -158,16 +162,18 @@ extern fn adc0_ss0(_ctxt: ADC0SS0) {
|
|||
|
||||
let mut loop_anode = LOOP_ANODE.borrow(cs).borrow_mut();
|
||||
let mut loop_cathode = LOOP_CATHODE.borrow(cs).borrow_mut();
|
||||
let mut electrometer = ELECTROMETER.borrow(cs).borrow_mut();
|
||||
loop_anode.adc_input(av_sample);
|
||||
loop_cathode.adc_input(fbi_sample, fd_sample, fv_sample, fbv_sample);
|
||||
electrometer.adc_input(ic_sample);
|
||||
|
||||
let time = TIME.borrow(cs);
|
||||
time.set(time.get() + 1);
|
||||
|
||||
if time.get() % 300 == 0 {
|
||||
loop_anode.debug_print();
|
||||
loop_cathode.debug_print();
|
||||
println!("{}", ic_sample);
|
||||
//loop_anode.get_status().debug_print();
|
||||
//loop_cathode.get_status().debug_print();
|
||||
electrometer.get_status().debug_print();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use core::num::Float;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Parameters {
|
||||
pub kp: f32,
|
||||
|
@ -13,9 +11,9 @@ pub struct Parameters {
|
|||
|
||||
pub struct Controller {
|
||||
parameters: Parameters,
|
||||
pub target: f32,
|
||||
target: f32,
|
||||
integral: f32,
|
||||
pub last_input: Option<f32>
|
||||
last_input: Option<f32>
|
||||
}
|
||||
|
||||
impl Controller {
|
||||
|
@ -58,12 +56,8 @@ impl Controller {
|
|||
output
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn is_within(&self, tolerance: f32) -> bool {
|
||||
match self.last_input {
|
||||
None => false,
|
||||
Some(last_input) => (last_input - self.target).abs() < tolerance
|
||||
}
|
||||
pub fn set_target(&mut self, target: f32) {
|
||||
self.target = target;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
|
Loading…
Reference in New Issue