dismantle ionpak, drive PWMs at fixed rate

This commit is contained in:
Astro 2019-07-30 15:35:56 +02:00
parent 1a00f5cf1a
commit 2ac3485b30
11 changed files with 89 additions and 852 deletions

110
firmware/Cargo.lock generated
View File

@ -5,6 +5,23 @@ name = "aligned"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "aligned"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"as-slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "as-slice"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
"stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bare-metal"
version = "0.2.4"
@ -15,7 +32,7 @@ dependencies = [
[[package]]
name = "bitflags"
version = "1.0.4"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -25,22 +42,33 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "byteorder"
version = "1.3.1"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cortex-m"
version = "0.5.8"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cortex-m"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"aligned 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cortex-m-rt"
version = "0.6.7"
version = "0.6.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cortex-m-rt-macros 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
@ -52,10 +80,10 @@ name = "cortex-m-rt-macros"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -66,14 +94,22 @@ dependencies = [
"build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "generic-array"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ionpak-firmware"
version = "1.0.0"
dependencies = [
"cortex-m 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-m-rt 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-m-rt 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libm 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=cd893e6)",
"tm4c129x 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"walkdir 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
@ -90,7 +126,7 @@ dependencies = [
[[package]]
name = "libm"
version = "0.1.2"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
@ -100,7 +136,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.4.27"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -108,10 +144,10 @@ dependencies = [
[[package]]
name = "quote"
version = "0.6.11"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@ -175,18 +211,23 @@ name = "smoltcp"
version = "0.4.0"
source = "git+https://github.com/m-labs/smoltcp?rev=cd893e6#cd893e6ab60f094d684b37be7bc013bf79f0459d"
dependencies = [
"bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"managed 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "stable_deref_trait"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.26"
version = "0.15.42"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -196,11 +237,16 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-m 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-m-rt 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)",
"cortex-m-rt 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)",
"vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "typenum"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.1.0"
@ -241,19 +287,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum aligned 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d39da9b88ae1a81c03c9c082b8db83f1d0e93914126041962af61034ab44c4a5"
"checksum aligned 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d3a316c7ea8e1e9ece54862c992def5a7ac14de9f5832b69d71760680efeeefa"
"checksum as-slice 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "293dac66b274fab06f95e7efb05ec439a6b70136081ea522d270bc351ae5bb27"
"checksum bare-metal 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a3caf393d93b2d453e80638d0674597020cef3382ada454faacd43d1a55a735a"
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
"checksum bitflags 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d155346769a6855b86399e9bc3814ab343cd3d62c7e985113d46a0ec3c281fd"
"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39"
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
"checksum cortex-m 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "dab2164a0fc216781a47fc343347365112ae6917421d3fa4bac6faf0fbaaaec7"
"checksum cortex-m-rt 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f69d2beca37acc3776c17201c9d1f8904fb9139fa3a4d2cf28c8436a07b21a88"
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum cortex-m 0.5.10 (registry+https://github.com/rust-lang/crates.io-index)" = "3c0b159a1e8306949579de3698c841dba58058197b65c60807194e4fa1e7a554"
"checksum cortex-m 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f3c18719fdc57db65668bfc977db9a0fa1a41d718c5d9cd4f652c9d4b0e0956a"
"checksum cortex-m-rt 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "17805910e3ecf029bdbfcc42b7384d9e3d9e5626153fa810002c1ef9839338ac"
"checksum cortex-m-rt-macros 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d7ae692573e0acccb1579fef1abf5a5bf1d2f3f0149a22b16870ec9309aee25f"
"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb"
"checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec"
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
"checksum libm 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "03c0bb6d5ce1b5cc6fd0578ec1cbc18c9d88b5b591a5c7c1d6c6175e266a0819"
"checksum libm 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
"checksum managed 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "43e2737ecabe4ae36a68061398bf27d2bfd0763f4c3c837a398478459494c4b7"
"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915"
"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
"checksum r0 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e2a38df5b15c8d5c7e8654189744d8e396bddc18ad48041a500ce52d6948941f"
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
"checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
@ -263,8 +313,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum smoltcp 0.4.0 (git+https://github.com/m-labs/smoltcp?rev=cd893e6)" = "<none>"
"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9"
"checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8"
"checksum syn 0.15.42 (registry+https://github.com/rust-lang/crates.io-index)" = "eadc09306ca51a40555dd6fc2b415538e9e18bc9f870e47b1a524a79fe2dcf5e"
"checksum tm4c129x 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d430ed4ed06dd9fff3d4517a37343e1b53789218f2f608bf1e0432f67abf624"
"checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum vcell 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "45c297f0afb6928cd08ab1ff9d95e99392595ea25ae1b5ecf822ff8764e57a0d"
"checksum volatile-register 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0d67cb4616d99b940db1d6bd28844ff97108b498a6ca850e5b6191a532063286"

View File

@ -25,7 +25,7 @@ const ERR_LATCHN: u8 = 0x20; // PL5
const BTNN: u8 = 0x80; // PL7
const ERR_RESN: u8 = 0x01; // PQ0
const PWM_LOAD: u16 = (/*pwmclk*/120_000_000u32 / /*freq*/100_000) as u16;
pub const PWM_LOAD: u16 = (/*pwmclk*/120_000_000u32 / /*freq*/100_000) as u16;
const UART_DIV: u32 = (((/*sysclk*/120_000_000 * 8) / /*baud*/115200) + 1) / 2;

View File

@ -1,87 +0,0 @@
use board;
pub struct Electrometer {
range: board::ElectrometerRange,
out_of_range_count: u8,
ignore_count: u8,
ic_buffer: f32,
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,
out_of_range_count: 0,
ignore_count: 0,
ic_buffer: 0.0,
ic_buffer_count: 0,
last_ic: None
}
}
pub fn adc_input(&mut self, ic_sample: u16) {
if self.ignore_count > 0 {
self.ignore_count -= 1;
} else {
let mut new_range = if ic_sample > 3100 {
match self.range {
board::ElectrometerRange::Low => Some(board::ElectrometerRange::Med),
board::ElectrometerRange::Med => Some(board::ElectrometerRange::High),
board::ElectrometerRange::High => None
}
} else if ic_sample < 105 {
match self.range {
board::ElectrometerRange::Low => None,
board::ElectrometerRange::Med => Some(board::ElectrometerRange::Low),
board::ElectrometerRange::High => Some(board::ElectrometerRange::Med)
}
} else {
None
};
if new_range.is_some() {
self.out_of_range_count += 1;
if self.out_of_range_count < 75 {
new_range = None;
}
} else {
self.out_of_range_count = 0;
}
if new_range.is_some() {
self.ignore_count = 150;
self.ic_buffer = 0.0;
self.ic_buffer_count = 0;
self.last_ic = None;
self.range = new_range.unwrap();
board::set_electrometer_range(self.range);
} else {
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 += ((ic_sample as f32) - board::IC_ADC_OFFSET)/gain;
self.ic_buffer_count += 1;
if self.ic_buffer_count == 512 {
self.last_ic = Some(self.ic_buffer/512.0);
self.ic_buffer = 0.0;
self.ic_buffer_count = 0;
}
}
}
}
pub fn get_status(&self) -> ElectrometerStatus {
ElectrometerStatus {
ic: self.last_ic
}
}
}

View File

@ -1,29 +0,0 @@
<!DOCTYPE html>
<html>
<title>ionpak</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<body>
<div class="w3-sidebar w3-light-grey w3-bar-block" style="width:15%">
<h3 class="w3-bar-item"><img src="logo.svg"></h3>
<a href="/" class="w3-bar-item w3-button">Measure</a>
<a href="/gauge_settings.html" class="w3-bar-item w3-button">Gauge settings</a>
<a href="/network_settings.html" class="w3-bar-item w3-button">Network settings</a>
<a href="/firmware.html" class="w3-bar-item w3-button">Firmware</a>
</div>
<div style="margin-left:15%">
<div class="w3-container w3-teal">
<h1>Firmware</h1>
</div>
<div class="w3-container">
<p>Firmware version: {version}</p>
</div>
</div>
</body>
</html>

View File

@ -1,180 +0,0 @@
use core::fmt;
const MAX_QUERY: usize = 128;
#[derive(Debug,Clone,Copy,PartialEq,Eq)]
enum State {
WaitG,
WaitE,
WaitT,
WaitSpace,
GetQuery,
WaitCR1,
WaitLF1,
WaitCR2,
WaitLF2,
Finished
}
pub struct Request {
state: State,
query_idx: usize,
query: [u8; MAX_QUERY]
}
impl Request {
pub fn new() -> Request {
Request {
state: State::WaitG,
query_idx: 0,
query: [0; MAX_QUERY]
}
}
pub fn reset(&mut self) {
self.state = State::WaitG;
self.query_idx = 0;
}
pub fn input_char(&mut self, c: u8) -> Result<bool, &'static str> {
match self.state {
State::WaitG => {
if c == b'G' {
self.state = State::WaitE;
} else {
return Err("invalid character in method")
}
}
State::WaitE => {
if c == b'E' {
self.state = State::WaitT;
} else {
return Err("invalid character in method")
}
}
State::WaitT => {
if c == b'T' {
self.state = State::WaitSpace;
} else {
return Err("invalid character in method")
}
}
State::WaitSpace => {
if c == b' ' {
self.state = State::GetQuery;
} else {
return Err("invalid character in method")
}
}
State::GetQuery => {
if c == b'\r' || c == b'\n' {
return Err("GET ended prematurely")
} else if c == b' ' {
if self.query_idx == 0 {
return Err("query is empty")
} else {
self.state = State::WaitCR1;
}
} else {
if self.query_idx >= self.query.len() {
return Err("query is too long")
} else {
self.query[self.query_idx] = c;
self.query_idx += 1;
}
}
}
State::WaitCR1 => {
if c == b'\r' {
self.state = State::WaitLF1;
}
}
State::WaitLF1 => {
if c == b'\n' {
self.state = State::WaitCR2;
} else {
self.state = State::WaitCR1;
}
}
State::WaitCR2 => {
if c == b'\r' {
self.state = State::WaitLF2;
} else {
self.state = State::WaitCR1;
}
}
State::WaitLF2 => {
if c == b'\n' {
self.state = State::Finished;
return Ok(true)
} else {
self.state = State::WaitCR1;
}
}
State::Finished => return Err("trailing characters")
}
Ok(false)
}
pub fn input(&mut self, buf: &[u8]) -> Result<bool, &'static str> {
let mut result = Ok(false);
for c in buf.iter() {
result = self.input_char(*c);
if result.is_err() {
return result;
}
}
result
}
pub fn get_query<'a>(&'a self) -> Result<&'a [u8], &'static str> {
if self.state != State::Finished {
return Err("request is not finished")
}
Ok(&self.query[..self.query_idx])
}
pub fn get_path<'a>(&'a self) -> Result<&'a [u8], &'static str> {
let query = self.get_query()?;
Ok(query.split(|b| *b == '?' as u8).next().unwrap())
}
// FIXME: this yields some empty strings
pub fn iter_args<'a>(&'a self) -> Result<impl Iterator<Item=(&'a [u8], &'a [u8])>, &'static str> {
let query = self.get_query()?;
let mut qs = query.split(|b| *b == '?' as u8);
qs.next();
let args = qs.next().unwrap_or(b"");
let args_it = args.split(|b| *b == '&' as u8);
Ok(args_it.map(|arg| {
let mut eqs = arg.split(|b| *b == '=' as u8);
(eqs.next().unwrap(), eqs.next().unwrap_or(b""))
}))
}
pub fn get_arg<'a>(&'a self, name: &[u8]) -> Result<&'a [u8], &'static str> {
for (current_name, current_value) in self.iter_args()? {
if current_name == name {
return Ok(current_value)
}
}
Err("argument not found")
}
}
pub fn write_reply_header(output: &mut fmt::Write, status: u16, content_type: &str, gzip: bool) -> fmt::Result {
let status_text = match status {
200 => "OK",
404 => "Not Found",
500 => "Internal Server Error",
_ => return Err(fmt::Error)
};
write!(output, "HTTP/1.1 {} {}\r\nContent-Type: {}\r\n",
status, status_text, content_type)?;
if gzip {
write!(output, "Content-Encoding: gzip\r\n")?;
write!(output, "Cache-Control: public, max-age=600\r\n")?;
}
write!(output, "\r\n")
}

View File

@ -1,57 +0,0 @@
<!DOCTYPE html>
<html>
<title>ionpak</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<body>
<div class="w3-sidebar w3-light-grey w3-bar-block" style="width:15%">
<h3 class="w3-bar-item"><img src="logo.svg"></h3>
<a href="/" class="w3-bar-item w3-button">Measure</a>
<a href="/gauge_settings.html" class="w3-bar-item w3-button">Gauge settings</a>
<a href="/network_settings.html" class="w3-bar-item w3-button">Network settings</a>
<a href="/firmware.html" class="w3-bar-item w3-button">Firmware</a>
</div>
<div style="margin-left:15%">
<div class="w3-container w3-teal">
<h1>Measure</h1>
</div>
<div class="w3-container">
<h3>Pressure</h3>
<div class="w3-card w3-xxlarge">
{pressure:.1e} mbar
</div>
</div>
<div class="w3-container">
<h3>Details</h3>
<table class="w3-table">
<tr><th>Parameter</th><th>Current</th><th>Target</th></tr>
<tr><td>Anode regulator ready</td><td>{anode_ready}</td></tr>
<tr><td>Anode voltage</td><td>{anode_av:.1}V</td></tr>
<tr><td>Cathode regulator ready</td><td>{cathode_ready}</td></tr>
<tr><td>Electron current</td><td>{cathode_fbi:.0}μA</td></tr>
<tr><td>Filament voltage</td><td>{cathode_fv:.2}V</td><td>{cathode_fv_target:.2}V</td></tr>
<tr><td>Cathode bias</td><td>{cathode_fbv:.1}V</td></tr>
<tr><td>Ion current</td><td>{ion_current:.5}nA</td></tr>
</table>
<p>
At local time:
<script language="Javascript">
var date = new Date();
var n = date.toDateString();
var time = date.toLocaleString();
document.write(time);
</script>
</p>
<p><a href="/measure.json">JSON</a></p>
</div>
</div>
</body>
</html>

View File

@ -1,69 +0,0 @@
use libm::F32Ext;
use board;
use pid;
const PID_PARAMETERS: pid::Parameters = pid::Parameters {
kp: 0.2,
ki: 0.05,
kd: 0.0,
output_min: 0.0,
output_max: 225.0,
integral_min: -700.0,
integral_max: 700.0
};
pub struct Controller {
pid: pid::Controller,
target: f32,
last_av: Option<f32>
}
#[derive(Clone, Copy)]
pub struct ControllerStatus {
pub ready: bool,
pub av: Option<f32>
}
impl Controller {
pub const fn new() -> Controller {
Controller {
pid: pid::Controller::new(PID_PARAMETERS),
target: 0.0,
last_av: None
}
}
pub fn adc_input(&mut self, av_sample: u16) {
let av = av_sample as f32/board::AV_ADC_GAIN;
self.last_av = Some(av);
let hv_pwm_duty = self.pid.update(av);
board::set_hv_pwm(hv_pwm_duty as u16)
}
pub fn set_target(&mut self, volts: f32) {
self.target = volts;
self.pid.set_target(volts);
}
fn ready(&self) -> bool {
match self.last_av {
None => false,
Some(last_av) => (last_av - self.target).abs() < 2.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
}
}
}

View File

@ -1,159 +0,0 @@
use libm::F32Ext;
use board;
use pid;
const FBI_PID_PARAMETERS: pid::Parameters = pid::Parameters {
kp: 200.0,
ki: 20.0,
kd: 10.0,
output_min: 0.5,
output_max: 10.0,
integral_min: -0.1,
integral_max: 0.1
};
const FV_PID_PARAMETERS: pid::Parameters = pid::Parameters {
kp: 20.0,
ki: 1.5,
kd: 0.0,
output_min: 0.0,
output_max: 150.0,
integral_min: -50.0,
integral_max: 50.0
};
pub struct Controller {
fbi_target: f32,
fbi_range: board::EmissionRange,
fbi_buffer: [f32; 16],
fbi_buffer_count: usize,
last_fbi: Option<f32>,
fbi_pid: pid::Controller,
last_fv_target: Option<f32>,
fv_pid: pid::Controller,
last_fv: Option<f32>,
fbv_target: f32,
last_fbv: Option<f32>
}
#[derive(Clone, Copy)]
pub struct ControllerStatus {
pub ready: bool,
pub fbi: Option<f32>,
pub fv_target: 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::Med,
fbi_buffer: [0.0; 16],
fbi_buffer_count: 0,
last_fbi: None,
fbi_pid: pid::Controller::new(FBI_PID_PARAMETERS),
last_fv_target: None,
fv_pid: pid::Controller::new(FV_PID_PARAMETERS),
last_fv: None,
fbv_target: 0.0,
last_fbv: None,
}
}
pub fn adc_input(&mut self, fbi_sample: u16, fd_sample: u16, fv_sample: u16, fbv_sample: u16) {
let fbi_voltage = ((fbi_sample as f32) - board::FBI_ADC_OFFSET)/board::FBI_ADC_GAIN;
let fbi_r225 = fbi_voltage/board::FBI_R225;
let fbi = match self.fbi_range {
board::EmissionRange::Low => fbi_r225,
board::EmissionRange::Med => {
let fd_voltage = ((fd_sample as f32) - board::FD_ADC_OFFSET)/board::FD_ADC_GAIN;
fbi_r225 + (fbi_voltage - fd_voltage)/board::FBI_R223
},
board::EmissionRange::High => {
let fd_voltage = 0.9;
fbi_r225 + (fbi_voltage - fd_voltage)/board::FBI_R224
}
};
self.fbi_buffer[self.fbi_buffer_count] = fbi;
self.fbi_buffer_count += 1;
if self.fbi_buffer_count == self.fbi_buffer.len() {
let mut fbi_avg: f32 = 0.0;
for fbi in self.fbi_buffer.iter() {
fbi_avg += *fbi;
}
self.last_fbi = Some(fbi_avg/(self.fbi_buffer.len() as f32));
self.fbi_buffer_count = 0;
}
let fv_target = self.fbi_pid.update(fbi);
self.last_fv_target = Some(fv_target);
self.fv_pid.set_target(fv_target);
let fv = fv_sample as f32/board::FV_ADC_GAIN;
let fv_pwm_duty = self.fv_pid.update(fv);
board::set_fv_pwm(120);
self.last_fv = Some(fv);
self.last_fbv = Some(fbv_sample as f32/board::FBV_ADC_GAIN);
}
pub fn set_emission_target(&mut self, amperes: f32) {
self.fbi_target = amperes;
self.fbi_pid.set_target(amperes);
self.fbi_range = board::EmissionRange::Low;
if amperes > 120e-6 {
self.fbi_range = board::EmissionRange::Med;
}
if amperes > 8e-3 {
self.fbi_range = board::EmissionRange::High;
}
board::set_emission_range(self.fbi_range);
}
pub fn set_bias_target(&mut self, volts: f32) {
self.fbv_target = volts;
board::set_fbv_pwm((volts/board::FBV_PWM_GAIN) as u16);
}
fn emission_ready(&self) -> bool {
match self.last_fbi {
None => false,
Some(last_fbi) => (self.fbi_target - last_fbi).abs()/self.fbi_target < 0.05
}
}
fn bias_ready(&self) -> bool {
match self.last_fbv {
None => false,
Some(last_fbv) => (self.fbv_target - last_fbv).abs() < 1.0
}
}
pub fn reset(&mut self) {
self.fbi_pid.reset();
self.fv_pid.reset();
self.last_fv_target = None;
self.fbi_buffer_count = 0;
self.last_fbi = None;
self.last_fv = None;
self.last_fbv = None;
}
pub fn get_status(&self) -> ControllerStatus {
ControllerStatus {
ready: self.emission_ready() & self.bias_ready(),
fbi: self.last_fbi,
fv_target: self.last_fv_target,
fv: self.last_fv,
fbv: self.last_fbv
}
}
}

View File

@ -45,12 +45,6 @@ mod board;
mod eeprom;
mod config;
mod ethmac;
mod pid;
mod loop_anode;
mod loop_cathode;
mod electrometer;
mod http;
mod pages;
static ADC_IRQ_COUNT: Mutex<Cell<u64>> = Mutex::new(Cell::new(0));
@ -61,16 +55,6 @@ fn get_time_ms() -> u64 {
adc_irq_count*24/125
}
static LOOP_ANODE: Mutex<RefCell<loop_anode::Controller>> = Mutex::new(RefCell::new(
loop_anode::Controller::new()));
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;
impl fmt::Write for UART0 {
@ -117,35 +101,6 @@ fn main() -> ! {
config.load();
}
cortex_m::interrupt::free(|cs| {
let mut loop_anode = LOOP_ANODE.borrow(cs).borrow_mut();
let mut loop_cathode = LOOP_CATHODE.borrow(cs).borrow_mut();
// ZJ-10
let anode = 165.0;
let cathode_bias = 50.0;
let emission = 0.5e-3;
// ZJ-27
/*let anode = 225.0;
let cathode_bias = 25.0;
let emission = 1.0e-3;*/
// ZJ-12
/*let anode = 200.0;
let cathode_bias = 50.0;
let emission = 4.0e-3;*/
// G8130
/*let anode = 180.0;
let cathode_bias = 30.0;
let emission = 4.0e-3;*/
loop_anode.set_target(anode);
loop_cathode.set_emission_target(emission);
loop_cathode.set_bias_target(cathode_bias);
});
println!(r#"
_ _
(_) | |
@ -195,7 +150,7 @@ fn main() -> ! {
create_socket!(sockets, tcp_rx_storage6, tcp_tx_storage6, tcp_handle6);
create_socket!(sockets, tcp_rx_storage7, tcp_tx_storage7, tcp_handle7);
let mut sessions = [
/*let mut sessions = [
(http::Request::new(), tcp_handle0),
(http::Request::new(), tcp_handle1),
(http::Request::new(), tcp_handle2),
@ -204,8 +159,11 @@ fn main() -> ! {
(http::Request::new(), tcp_handle5),
(http::Request::new(), tcp_handle6),
(http::Request::new(), tcp_handle7),
];
];*/
board::set_hv_pwm(board::PWM_LOAD / 2);
board::set_fv_pwm(board::PWM_LOAD / 2);
board::set_fbv_pwm(board::PWM_LOAD / 2);
board::start_adc();
let mut fast_blink_count = if button_pressed { 40 } else { 0 };
@ -215,7 +173,7 @@ fn main() -> ! {
loop {
let time = get_time_ms();
for &mut(ref mut request, tcp_handle) in sessions.iter_mut() {
/*for &mut(ref mut request, tcp_handle) in sessions.iter_mut() {
let socket = &mut *sockets.get::<TcpSocket>(tcp_handle);
if !socket.is_open() {
socket.listen(80).unwrap()
@ -241,7 +199,7 @@ fn main() -> ! {
request.reset();
socket.close();
}
}
}*/
match iface.poll(&mut sockets, Instant::from_millis(time as i64)) {
Ok(_) => (),
Err(e) => println!("poll error: {}", e)
@ -268,11 +226,6 @@ fn main() -> ! {
Some(t) => if time > t {
latch_reset_time = None;
cortex_m::interrupt::free(|cs| {
// 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");
@ -298,13 +251,6 @@ fn adc0_ss0() {
let av_sample = adc0.ssfifo0.read().data().bits();
let fbv_sample = adc0.ssfifo0.read().data().bits();
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 adc_irq_count = ADC_IRQ_COUNT.borrow(cs);
adc_irq_count.set(adc_irq_count.get() + 1);
});

View File

@ -1,39 +0,0 @@
<!DOCTYPE html>
<html>
<title>ionpak</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="style.css">
<body>
<div class="w3-sidebar w3-light-grey w3-bar-block" style="width:15%">
<h3 class="w3-bar-item"><img src="logo.svg"></h3>
<a href="/" class="w3-bar-item w3-button">Measure</a>
<a href="/gauge_settings.html" class="w3-bar-item w3-button">Gauge settings</a>
<a href="/network_settings.html" class="w3-bar-item w3-button">Network settings</a>
<a href="/firmware.html" class="w3-bar-item w3-button">Firmware</a>
</div>
<div style="margin-left:15%">
<div class="w3-container w3-teal">
<h1>Network settings</h1>
</div>
<div class="w3-container">
<p>{status}</p>
</div>
<div class="w3-container">
<form class="w3-container w3-card-4" method="GET">
<p>
<label>IP address</label>
<input class="w3-input w3-border" name="ip" type="text" value="{ip}"></p>
<p>
<button class="w3-btn w3-blue">Update</button></p>
</form>
</div>
</div>
</body>
</html>

View File

@ -1,141 +0,0 @@
use core::fmt;
use core::fmt::Write;
use core::cell::RefCell;
use core::str;
use cortex_m;
use cortex_m::interrupt::Mutex;
use smoltcp::wire::IpCidr;
use smoltcp::socket::TcpSocket;
use http;
use config;
use loop_anode;
use loop_cathode;
use electrometer;
macro_rules! opn_fmt {
($struct_name:ident, $error:expr) => {
struct $struct_name(Option<f32>);
impl fmt::Display for $struct_name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
None => f.write_str($error),
Some(x) => x.fmt(f)
}
}
}
impl fmt::LowerExp for $struct_name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self.0 {
None => f.write_str($error),
Some(x) => x.fmt(f)
}
}
}
}
}
opn_fmt!(OpnFmt, "ERROR");
opn_fmt!(OpnFmtJSON, "null");
pub fn serve(output: &mut TcpSocket, request: &http::Request,
config: &mut config::Config,
loop_anode_m: &Mutex<RefCell<loop_anode::Controller>>,
loop_cathode_m: &Mutex<RefCell<loop_cathode::Controller>>,
electrometer_m: &Mutex<RefCell<electrometer::Electrometer>>) {
match request.get_path().unwrap() {
b"/" => {
let (anode, cathode, electrometer) = cortex_m::interrupt::free(|cs| {
(loop_anode_m.borrow(cs).borrow().get_status(),
loop_cathode_m.borrow(cs).borrow().get_status(),
electrometer_m.borrow(cs).borrow().get_status())
});
let pressure = electrometer.ic.and_then(|ic| {
if ic > 1.0e-12 {
cathode.fbi.and_then(|fbi| Some(ic/fbi/18.75154))
} else {
None
}
});
http::write_reply_header(output, 200, "text/html; charset=utf-8", false).unwrap();
write!(output, include_str!("index.html"),
pressure=OpnFmt(pressure),
anode_ready=anode.ready,
anode_av=OpnFmt(anode.av),
cathode_ready=cathode.ready,
cathode_fbi=OpnFmt(cathode.fbi.and_then(|x| Some(x*1.0e6))),
cathode_fv=OpnFmt(cathode.fv),
cathode_fv_target=OpnFmt(cathode.fv_target),
cathode_fbv=OpnFmt(cathode.fbv),
ion_current=OpnFmt(electrometer.ic.and_then(|x| Some(x*1.0e9)))).unwrap();
},
b"/measure.json" => {
let (cathode, electrometer) = cortex_m::interrupt::free(|cs| {
(loop_cathode_m.borrow(cs).borrow().get_status(),
electrometer_m.borrow(cs).borrow().get_status())
});
// TODO: factor this
let pressure = electrometer.ic.and_then(|ic| {
if ic > 1.0e-12 {
cathode.fbi.and_then(|fbi| Some(ic/fbi/18.75154))
} else {
None
}
});
http::write_reply_header(output, 200, "application/json", false).unwrap();
write!(output, "{{\"pressure\": {:.1e}, \"current\": {:.3e}}}",
OpnFmtJSON(pressure), OpnFmtJSON(electrometer.ic)).unwrap();
}
b"/network_settings.html" => {
let mut status = "";
let ip_arg = request.get_arg(b"ip");
if ip_arg.is_ok() {
let ip_arg = str::from_utf8(ip_arg.unwrap());
if ip_arg.is_ok() {
let mut ip_arg = ip_arg.unwrap().split("%2F");
let ip = ip_arg.next().map(|x| x.parse());
let cidr = ip_arg.next().map(|x| x.parse());
match (ip, cidr) {
(Some(Ok(ip)), Some(Ok(cidr))) => {
status = "IP address has been updated and will be active after a reboot.";
config.ip = IpCidr::new(ip, cidr);
config.save();
}
_ =>
status = "failed to parse IP address"
}
} else {
status = "IP address contains an invalid UTF-8 character";
}
}
http::write_reply_header(output, 200, "text/html; charset=utf-8", false).unwrap();
write!(output, include_str!("network_settings.html"),
status=status, ip=config.ip).unwrap();
},
b"/firmware.html" => {
http::write_reply_header(output, 200, "text/html; charset=utf-8", false).unwrap();
write!(output, include_str!("firmware.html"),
version=include_str!(concat!(env!("OUT_DIR"), "/git-describe"))).unwrap();
}
b"/style.css" => {
let data = include_bytes!("style.css.gz");
http::write_reply_header(output, 200, "text/css", true).unwrap();
output.send_slice(data).unwrap();
},
b"/logo.svg" => {
let data = include_bytes!("logo.svg.gz");
http::write_reply_header(output, 200, "image/svg+xml", true).unwrap();
output.send_slice(data).unwrap();
},
_ => {
http::write_reply_header(output, 404, "text/plain", false).unwrap();
write!(output, "Not found").unwrap();
}
}
}