diff --git a/firmware/src/board.rs b/firmware/src/board.rs index 3ca58c6..47180d7 100644 --- a/firmware/src/board.rs +++ b/firmware/src/board.rs @@ -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); diff --git a/firmware/src/electrometer.rs b/firmware/src/electrometer.rs new file mode 100644 index 0000000..156be27 --- /dev/null +++ b/firmware/src/electrometer.rs @@ -0,0 +1,56 @@ +use board; + +pub struct Electrometer { + range: board::ElectrometerRange, + ic_buffer: [f32; 16], + ic_buffer_count: usize, + last_ic: Option +} + +#[derive(Clone, Copy)] +pub struct ElectrometerStatus { + pub ic: Option +} + +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()); + } + } +} diff --git a/firmware/src/loop_anode.rs b/firmware/src/loop_anode.rs index b5a5781..1b9577f 100644 --- a/firmware/src/loop_anode.rs +++ b/firmware/src/loop_anode.rs @@ -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 } +pub struct ControllerStatus { + pub ready: bool, + pub av: Option +} 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 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); + 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.av.is_some() { + println!("voltage: {}V", self.av.unwrap()); } } } diff --git a/firmware/src/loop_cathode.rs b/firmware/src/loop_cathode.rs index 7f98104..fb70e3d 100644 --- a/firmware/src/loop_cathode.rs +++ b/firmware/src/loop_cathode.rs @@ -27,12 +27,20 @@ pub struct Controller { last_fbv: Option } +#[derive(Clone, Copy)] +pub struct ControllerStatus { + pub ready: bool, + pub fbi: Option, + pub fv: Option, + pub fbv: Option +} + 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()); } } } diff --git a/firmware/src/main.rs b/firmware/src/main.rs index cabcba9..5863814 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -31,6 +31,7 @@ mod board; mod pid; mod loop_anode; mod loop_cathode; +mod electrometer; static TIME: Mutex> = Mutex::new(Cell::new(0)); @@ -46,6 +47,9 @@ static LOOP_ANODE: Mutex> = Mutex::new(RefCell:: static LOOP_CATHODE: Mutex> = Mutex::new(RefCell::new( loop_cathode::Controller::new())); +static ELECTROMETER: Mutex> = 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(); } }); } diff --git a/firmware/src/pid.rs b/firmware/src/pid.rs index 91dbb54..b5309a5 100644 --- a/firmware/src/pid.rs +++ b/firmware/src/pid.rs @@ -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 + last_input: Option } 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)]