forked from M-Labs/thermostat
implement all the tcp commands
This commit is contained in:
parent
cee0a1fcab
commit
da4aaf4ff6
|
@ -14,6 +14,8 @@ pub const SPI_MODE: spi::Mode = spi::Mode {
|
||||||
/// 30 MHz
|
/// 30 MHz
|
||||||
pub const SPI_CLOCK: MegaHertz = MegaHertz(30);
|
pub const SPI_CLOCK: MegaHertz = MegaHertz(30);
|
||||||
|
|
||||||
|
pub const MAX_VALUE: u32 = 0x20000;
|
||||||
|
|
||||||
/// [AD5680](https://www.analog.com/media/en/technical-documentation/data-sheets/AD5680.pdf) DAC
|
/// [AD5680](https://www.analog.com/media/en/technical-documentation/data-sheets/AD5680.pdf) DAC
|
||||||
pub struct Dac<SPI: Transfer<u8>, S: OutputPin> {
|
pub struct Dac<SPI: Transfer<u8>, S: OutputPin> {
|
||||||
spi: SPI,
|
spi: SPI,
|
||||||
|
|
|
@ -85,30 +85,28 @@ pub enum PidParameter {
|
||||||
/// Steinhart-Hart equation parameter
|
/// Steinhart-Hart equation parameter
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum ShParameter {
|
pub enum ShParameter {
|
||||||
A,
|
T0,
|
||||||
B,
|
R,
|
||||||
C,
|
R0,
|
||||||
ParallelR,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub struct PwmConfig {
|
pub enum PwmPin {
|
||||||
pub width: u16,
|
ISet,
|
||||||
pub total: u16,
|
MaxIPos,
|
||||||
|
MaxINeg,
|
||||||
|
MaxV,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
impl PwmPin {
|
||||||
pub enum PwmMode {
|
pub fn name(&self) -> &'static str {
|
||||||
Manual(PwmConfig),
|
match self {
|
||||||
Pid,
|
PwmPin::ISet => "i_set",
|
||||||
|
PwmPin::MaxIPos => "max_i_pos",
|
||||||
|
PwmPin::MaxINeg => "max_i_neg",
|
||||||
|
PwmPin::MaxV => "max_v",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
pub enum PwmSetup {
|
|
||||||
ISet(PwmMode),
|
|
||||||
MaxIPos(PwmConfig),
|
|
||||||
MaxINeg(PwmConfig),
|
|
||||||
MaxV(PwmConfig),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
@ -116,10 +114,17 @@ pub enum Command {
|
||||||
Quit,
|
Quit,
|
||||||
Show(ShowCommand),
|
Show(ShowCommand),
|
||||||
Reporting(bool),
|
Reporting(bool),
|
||||||
|
/// PWM parameter setting
|
||||||
Pwm {
|
Pwm {
|
||||||
channel: usize,
|
channel: usize,
|
||||||
setup: PwmSetup,
|
pin: PwmPin,
|
||||||
|
duty: u32,
|
||||||
},
|
},
|
||||||
|
/// Enable PID control for `i_set`
|
||||||
|
PwmPid {
|
||||||
|
channel: usize,
|
||||||
|
},
|
||||||
|
/// PID parameter setting
|
||||||
Pid {
|
Pid {
|
||||||
channel: usize,
|
channel: usize,
|
||||||
parameter: PidParameter,
|
parameter: PidParameter,
|
||||||
|
@ -149,7 +154,7 @@ fn whitespace(input: &[u8]) -> IResult<&[u8], ()> {
|
||||||
fold_many1(char(' '), (), |(), _| ())(input)
|
fold_many1(char(' '), (), |(), _| ())(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unsigned(input: &[u8]) -> IResult<&[u8], Result<u16, Error>> {
|
fn unsigned(input: &[u8]) -> IResult<&[u8], Result<u32, Error>> {
|
||||||
take_while1(is_digit)(input)
|
take_while1(is_digit)(input)
|
||||||
.map(|(input, digits)| {
|
.map(|(input, digits)| {
|
||||||
let result = lexical::parse(digits)
|
let result = lexical::parse(digits)
|
||||||
|
@ -202,82 +207,77 @@ fn report(input: &[u8]) -> IResult<&[u8], Command> {
|
||||||
)(input)
|
)(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `pwm ... <width> <total>` - Set pwm duty cycle
|
fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<(PwmPin, u32), Error>> {
|
||||||
fn pwm_config(input: &[u8]) -> IResult<&[u8], Result<PwmConfig, Error>> {
|
let result_with_pin = |pin: PwmPin|
|
||||||
let (input, width) = unsigned(input)?;
|
move |result: Result<u32, Error>|
|
||||||
let width = match width {
|
result.map(|duty| (pin, duty));
|
||||||
Ok(width) => width,
|
|
||||||
Err(e) => return Ok((input, Err(e.into()))),
|
|
||||||
};
|
|
||||||
let (input, _) = whitespace(input)?;
|
|
||||||
let (input, total) = unsigned(input)?;
|
|
||||||
let total = match total {
|
|
||||||
Ok(total) => total,
|
|
||||||
Err(e) => return Ok((input, Err(e.into()))),
|
|
||||||
};
|
|
||||||
Ok((input, Ok(PwmConfig { width, total })))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result<PwmSetup, Error>> {
|
|
||||||
alt((
|
alt((
|
||||||
map(
|
map(
|
||||||
preceded(
|
preceded(
|
||||||
tag("max_i_pos"),
|
tag("max_i_pos"),
|
||||||
preceded(
|
preceded(
|
||||||
whitespace,
|
whitespace,
|
||||||
pwm_config
|
unsigned
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|result| result.map(PwmSetup::MaxIPos)
|
result_with_pin(PwmPin::MaxIPos)
|
||||||
),
|
),
|
||||||
map(
|
map(
|
||||||
preceded(
|
preceded(
|
||||||
tag("max_i_neg"),
|
tag("max_i_neg"),
|
||||||
preceded(
|
preceded(
|
||||||
whitespace,
|
whitespace,
|
||||||
pwm_config
|
unsigned
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|result| result.map(PwmSetup::MaxINeg)
|
result_with_pin(PwmPin::MaxINeg)
|
||||||
),
|
),
|
||||||
map(
|
map(
|
||||||
preceded(
|
preceded(
|
||||||
tag("max_v"),
|
tag("max_v"),
|
||||||
preceded(
|
preceded(
|
||||||
whitespace,
|
whitespace,
|
||||||
pwm_config
|
unsigned
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|result| result.map(PwmSetup::MaxV)
|
result_with_pin(PwmPin::MaxV)
|
||||||
),
|
),
|
||||||
map(pwm_config, |result| result.map(|config| {
|
map(unsigned, result_with_pin(PwmPin::ISet)
|
||||||
PwmSetup::ISet(PwmMode::Manual(config))
|
))
|
||||||
}))
|
)(input)
|
||||||
))(input)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `pwm <0-1> pid` - Set PWM to be controlled by PID
|
/// `pwm <0-1> pid` - Set PWM to be controlled by PID
|
||||||
fn pwm_pid(input: &[u8]) -> IResult<&[u8], Result<PwmSetup, Error>> {
|
fn pwm_pid(input: &[u8]) -> IResult<&[u8], ()> {
|
||||||
value(Ok(PwmSetup::ISet(PwmMode::Pid)), tag("pid"))(input)
|
value((), tag("pid"))(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
|
||||||
let (input, _) = tag("pwm")(input)?;
|
let (input, _) = tag("pwm")(input)?;
|
||||||
alt((
|
alt((
|
||||||
preceded(
|
|input| {
|
||||||
whitespace,
|
let (input, _) = whitespace(input)?;
|
||||||
map(
|
let (input, channel) = channel(input)?;
|
||||||
separated_pair(
|
let (input, _) = whitespace(input)?;
|
||||||
channel,
|
let (input, result) = alt((
|
||||||
whitespace,
|
|input| {
|
||||||
alt((
|
let (input, ()) = pwm_pid(input)?;
|
||||||
pwm_pid,
|
Ok((input, Ok(Command::PwmPid { channel })))
|
||||||
pwm_setup
|
},
|
||||||
))
|
|input| {
|
||||||
),
|
let (input, config) = pwm_setup(input)?;
|
||||||
|(channel, setup)| setup.map(|setup| Command::Pwm { channel, setup })
|
match config {
|
||||||
)
|
Ok((pin, duty)) =>
|
||||||
),
|
Ok((input, Ok(Command::Pwm { channel, pin, duty }))),
|
||||||
|
Err(e) =>
|
||||||
|
Ok((input, Err(e))),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
))(input)?;
|
||||||
|
end(input)?;
|
||||||
|
Ok((input, result))
|
||||||
|
},
|
||||||
value(Ok(Command::Show(ShowCommand::Pwm)), end)
|
value(Ok(Command::Show(ShowCommand::Pwm)), end)
|
||||||
))(input)
|
))(input)
|
||||||
}
|
}
|
||||||
|
@ -320,10 +320,9 @@ fn steinhart_hart_parameter(input: &[u8]) -> IResult<&[u8], Result<Command, Erro
|
||||||
let (input, channel) = channel(input)?;
|
let (input, channel) = channel(input)?;
|
||||||
let (input, _) = whitespace(input)?;
|
let (input, _) = whitespace(input)?;
|
||||||
let (input, parameter) =
|
let (input, parameter) =
|
||||||
alt((value(ShParameter::A, tag("a")),
|
alt((value(ShParameter::T0, tag("t0")),
|
||||||
value(ShParameter::B, tag("b")),
|
value(ShParameter::R, tag("r")),
|
||||||
value(ShParameter::C, tag("c")),
|
value(ShParameter::R0, tag("r0"))
|
||||||
value(ShParameter::ParallelR, tag("parallel_r"))
|
|
||||||
))(input)?;
|
))(input)?;
|
||||||
let (input, _) = whitespace(input)?;
|
let (input, _) = whitespace(input)?;
|
||||||
let (input, value) = float(input)?;
|
let (input, value) = float(input)?;
|
||||||
|
@ -426,58 +425,50 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_pwm_manual() {
|
fn parse_pwm_manual() {
|
||||||
let command = Command::parse(b"pwm 1 16383 65535");
|
let command = Command::parse(b"pwm 1 16383");
|
||||||
assert_eq!(command, Ok(Command::Pwm {
|
assert_eq!(command, Ok(Command::Pwm {
|
||||||
channel: 1,
|
channel: 1,
|
||||||
setup: PwmSetup::ISet(PwmMode::Manual(PwmConfig {
|
pin: PwmPin::ISet,
|
||||||
width: 16383,
|
duty: 16383,
|
||||||
total: 65535,
|
|
||||||
})),
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_pwm_pid() {
|
fn parse_pwm_pid() {
|
||||||
let command = Command::parse(b"pwm 0 pid");
|
let command = Command::parse(b"pwm 0 pid");
|
||||||
assert_eq!(command, Ok(Command::Pwm {
|
assert_eq!(command, Ok(Command::PwmPid {
|
||||||
channel: 0,
|
channel: 0,
|
||||||
setup: PwmSetup::ISet(PwmMode::Pid),
|
pin: PwmPin::ISet,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_pwm_max_i_pos() {
|
fn parse_pwm_max_i_pos() {
|
||||||
let command = Command::parse(b"pwm 0 max_i_pos 7 13");
|
let command = Command::parse(b"pwm 0 max_i_pos 7");
|
||||||
assert_eq!(command, Ok(Command::Pwm {
|
assert_eq!(command, Ok(Command::Pwm {
|
||||||
channel: 0,
|
channel: 0,
|
||||||
setup: PwmSetup::MaxIPos(PwmConfig {
|
pin: PwmPin::MaxIPos,
|
||||||
width: 7,
|
duty: 7,
|
||||||
total: 13,
|
|
||||||
}),
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_pwm_max_i_neg() {
|
fn parse_pwm_max_i_neg() {
|
||||||
let command = Command::parse(b"pwm 0 max_i_neg 128 65535");
|
let command = Command::parse(b"pwm 0 max_i_neg 128");
|
||||||
assert_eq!(command, Ok(Command::Pwm {
|
assert_eq!(command, Ok(Command::Pwm {
|
||||||
channel: 0,
|
channel: 0,
|
||||||
setup: PwmSetup::MaxINeg(PwmConfig {
|
pin: PwmPin::MaxINeg,
|
||||||
width: 128,
|
duty: 128,
|
||||||
total: 65535,
|
|
||||||
}),
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_pwm_max_v() {
|
fn parse_pwm_max_v() {
|
||||||
let command = Command::parse(b"pwm 0 max_v 32768 65535");
|
let command = Command::parse(b"pwm 0 max_v 32768");
|
||||||
assert_eq!(command, Ok(Command::Pwm {
|
assert_eq!(command, Ok(Command::Pwm {
|
||||||
channel: 0,
|
channel: 0,
|
||||||
setup: PwmSetup::MaxV(PwmConfig {
|
pin: PwmPin::MaxV,
|
||||||
width: 32768,
|
duty: 32768,
|
||||||
total: 65535,
|
|
||||||
}),
|
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -515,10 +506,10 @@ mod test {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_steinhart_hart_parallel_r() {
|
fn parse_steinhart_hart_parallel_r() {
|
||||||
let command = Command::parse(b"s-h 1 parallel_r 23.05");
|
let command = Command::parse(b"s-h 1 t0 23.05");
|
||||||
assert_eq!(command, Ok(Command::SteinhartHart {
|
assert_eq!(command, Ok(Command::SteinhartHart {
|
||||||
channel: 1,
|
channel: 1,
|
||||||
parameter: ShParameter::ParallelR,
|
parameter: ShParameter::T0,
|
||||||
value: 23.05,
|
value: 23.05,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
359
src/main.rs
359
src/main.rs
|
@ -9,11 +9,15 @@ use panic_semihosting as _;
|
||||||
|
|
||||||
use log::{info, warn};
|
use log::{info, warn};
|
||||||
|
|
||||||
|
use core::ops::DerefMut;
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use cortex_m::asm::wfi;
|
use cortex_m::asm::wfi;
|
||||||
use cortex_m_rt::entry;
|
use cortex_m_rt::entry;
|
||||||
use embedded_hal::watchdog::{WatchdogEnable, Watchdog};
|
|
||||||
use stm32f4xx_hal::{
|
use stm32f4xx_hal::{
|
||||||
|
hal::{
|
||||||
|
self,
|
||||||
|
watchdog::{WatchdogEnable, Watchdog},
|
||||||
|
},
|
||||||
rcc::RccExt,
|
rcc::RccExt,
|
||||||
watchdog::IndependentWatchdog,
|
watchdog::IndependentWatchdog,
|
||||||
time::{U32Ext, MegaHertz},
|
time::{U32Ext, MegaHertz},
|
||||||
|
@ -36,16 +40,20 @@ use server::Server;
|
||||||
mod session;
|
mod session;
|
||||||
use session::{CHANNELS, Session, SessionOutput};
|
use session::{CHANNELS, Session, SessionOutput};
|
||||||
mod command_parser;
|
mod command_parser;
|
||||||
use command_parser::{Command, ShowCommand, PwmSetup, PwmMode};
|
use command_parser::{Command, ShowCommand, PwmPin};
|
||||||
mod timer;
|
mod timer;
|
||||||
mod pid;
|
mod pid;
|
||||||
mod steinhart_hart;
|
mod steinhart_hart;
|
||||||
|
use steinhart_hart as sh;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug)]
|
|
||||||
struct ChannelState {
|
struct ChannelState {
|
||||||
adc_data: Option<i32>,
|
adc_data: Option<i32>,
|
||||||
adc_time: Instant,
|
adc_time: Instant,
|
||||||
|
dac_value: u32,
|
||||||
|
pid_enabled: bool,
|
||||||
|
pid: pid::Controller,
|
||||||
|
sh: sh::Parameters,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ChannelState {
|
impl Default for ChannelState {
|
||||||
|
@ -53,11 +61,16 @@ impl Default for ChannelState {
|
||||||
ChannelState {
|
ChannelState {
|
||||||
adc_data: None,
|
adc_data: None,
|
||||||
adc_time: Instant::from_secs(0),
|
adc_time: Instant::from_secs(0),
|
||||||
|
dac_value: 0,
|
||||||
|
pid_enabled: false,
|
||||||
|
pid: pid::Controller::new(pid::Parameters::default()),
|
||||||
|
sh: sh::Parameters::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const HSE: MegaHertz = MegaHertz(8);
|
||||||
#[cfg(not(feature = "semihosting"))]
|
#[cfg(not(feature = "semihosting"))]
|
||||||
const WATCHDOG_INTERVAL: u32 = 100;
|
const WATCHDOG_INTERVAL: u32 = 100;
|
||||||
#[cfg(feature = "semihosting")]
|
#[cfg(feature = "semihosting")]
|
||||||
|
@ -65,12 +78,9 @@ const WATCHDOG_INTERVAL: u32 = 10_000;
|
||||||
|
|
||||||
#[cfg(not(feature = "generate-hwaddr"))]
|
#[cfg(not(feature = "generate-hwaddr"))]
|
||||||
const NET_HWADDR: [u8; 6] = [0x02, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
|
const NET_HWADDR: [u8; 6] = [0x02, 0x00, 0xDE, 0xAD, 0xBE, 0xEF];
|
||||||
|
|
||||||
const TCP_PORT: u16 = 23;
|
const TCP_PORT: u16 = 23;
|
||||||
|
|
||||||
|
|
||||||
const HSE: MegaHertz = MegaHertz(8);
|
|
||||||
|
|
||||||
/// Initialization and main loop
|
/// Initialization and main loop
|
||||||
#[entry]
|
#[entry]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
|
@ -105,9 +115,10 @@ fn main() -> ! {
|
||||||
|
|
||||||
let mut adc = ad7172::Adc::new(pins.adc_spi, pins.adc_nss).unwrap();
|
let mut adc = ad7172::Adc::new(pins.adc_spi, pins.adc_nss).unwrap();
|
||||||
let mut dac0 = ad5680::Dac::new(pins.dac0_spi, pins.dac0_sync);
|
let mut dac0 = ad5680::Dac::new(pins.dac0_spi, pins.dac0_sync);
|
||||||
dac0.set(0);
|
dac0.set(0).unwrap();
|
||||||
let mut dac1 = ad5680::Dac::new(pins.dac1_spi, pins.dac1_sync);
|
let mut dac1 = ad5680::Dac::new(pins.dac1_spi, pins.dac1_sync);
|
||||||
dac1.set(0);
|
dac1.set(0).unwrap();
|
||||||
|
let mut pwm = pins.pwm;
|
||||||
|
|
||||||
timer::setup(cp.SYST, clocks);
|
timer::setup(cp.SYST, clocks);
|
||||||
|
|
||||||
|
@ -120,7 +131,9 @@ fn main() -> ! {
|
||||||
};
|
};
|
||||||
info!("Net hwaddr: {}", hwaddr);
|
info!("Net hwaddr: {}", hwaddr);
|
||||||
|
|
||||||
let mut channel_states = [ChannelState::default(); CHANNELS];
|
let mut channel_states: [ChannelState; CHANNELS] = [
|
||||||
|
ChannelState::default(), ChannelState::default()
|
||||||
|
];
|
||||||
|
|
||||||
net::run(dp.ETHERNET_MAC, dp.ETHERNET_DMA, hwaddr, |iface| {
|
net::run(dp.ETHERNET_MAC, dp.ETHERNET_DMA, hwaddr, |iface| {
|
||||||
Server::<Session>::run(iface, |server| {
|
Server::<Session>::run(iface, |server| {
|
||||||
|
@ -167,175 +180,213 @@ fn main() -> ! {
|
||||||
socket, "t={} raw{}=0x{:06X}",
|
socket, "t={} raw{}=0x{:06X}",
|
||||||
state.adc_time, channel, adc_data
|
state.adc_time, channel, adc_data
|
||||||
);
|
);
|
||||||
// TODO: show pwm status et al
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Command::Show(ShowCommand::Pid) => {
|
Command::Show(ShowCommand::Pid) => {
|
||||||
// for (channel, state) in states.iter().enumerate() {
|
for (channel, state) in channel_states.iter().enumerate() {
|
||||||
// let _ = writeln!(socket, "PID settings for channel {}", channel);
|
let _ = writeln!(socket, "PID settings for channel {}", channel);
|
||||||
// let pid = &state.pid;
|
let pid = &state.pid;
|
||||||
// let _ = writeln!(socket, "- target={:.4}", pid.get_target());
|
let _ = writeln!(socket, "- target={:.4}", pid.get_target());
|
||||||
// let p = pid.get_parameters();
|
let p = pid.get_parameters();
|
||||||
// macro_rules! out {
|
macro_rules! out {
|
||||||
// ($p: tt) => {
|
($p: tt) => {
|
||||||
// let _ = writeln!(socket, "- {}={:.4}", stringify!($p), p.$p);
|
let _ = writeln!(socket, "- {}={:.4}", stringify!($p), p.$p);
|
||||||
// };
|
};
|
||||||
// }
|
}
|
||||||
// out!(kp);
|
out!(kp);
|
||||||
// out!(ki);
|
out!(ki);
|
||||||
// out!(kd);
|
out!(kd);
|
||||||
// out!(output_min);
|
out!(output_min);
|
||||||
// out!(output_max);
|
out!(output_max);
|
||||||
// out!(integral_min);
|
out!(integral_min);
|
||||||
// out!(integral_max);
|
out!(integral_max);
|
||||||
// let _ = writeln!(socket, "");
|
let _ = writeln!(socket, "");
|
||||||
// }
|
}
|
||||||
}
|
}
|
||||||
Command::Show(ShowCommand::Pwm) => {
|
Command::Show(ShowCommand::Pwm) => {
|
||||||
// for (channel, state) in states.iter().enumerate() {
|
for (channel, state) in channel_states.iter().enumerate() {
|
||||||
// let _ = writeln!(
|
let _ = writeln!(
|
||||||
// socket, "channel {}: PID={}",
|
socket, "channel {}: PID={}",
|
||||||
// channel,
|
channel,
|
||||||
// if state.pid_enabled { "engaged" } else { "disengaged" }
|
if state.pid_enabled { "engaged" } else { "disengaged" }
|
||||||
// );
|
);
|
||||||
// for pin in TecPin::VALID_VALUES {
|
let _ = writeln!(socket, "- i_set={}/{}", state.dac_value, ad5680::MAX_VALUE);
|
||||||
// let (width, total) = match channel {
|
fn show_pwm_channel<S, P>(mut socket: S, name: &str, pin: &P)
|
||||||
// 0 => tec0.get(*pin),
|
where
|
||||||
// 1 => tec1.get(*pin),
|
S: core::fmt::Write,
|
||||||
// _ => unreachable!(),
|
P: hal::PwmPin<Duty=u16>,
|
||||||
// };
|
{
|
||||||
// let _ = writeln!(socket, "- {}={}/{}", pin, width, total);
|
let _ = writeln!(
|
||||||
// }
|
socket,
|
||||||
// let _ = writeln!(socket, "");
|
"- {}={}/{}",
|
||||||
// }
|
name, pin.get_duty(), pin.get_max_duty()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
match channel {
|
||||||
|
0 => {
|
||||||
|
show_pwm_channel(socket.deref_mut(), "max_v", &pwm.max_v0);
|
||||||
|
show_pwm_channel(socket.deref_mut(), "max_i_pos", &pwm.max_i_pos0);
|
||||||
|
show_pwm_channel(socket.deref_mut(), "max_i_neg", &pwm.max_i_neg0);
|
||||||
|
}
|
||||||
|
1 => {
|
||||||
|
show_pwm_channel(socket.deref_mut(), "max_v", &pwm.max_v1);
|
||||||
|
show_pwm_channel(socket.deref_mut(), "max_i_pos", &pwm.max_i_pos1);
|
||||||
|
show_pwm_channel(socket.deref_mut(), "max_i_neg", &pwm.max_i_neg1);
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
let _ = writeln!(socket, "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Command::Show(ShowCommand::SteinhartHart) => {
|
Command::Show(ShowCommand::SteinhartHart) => {
|
||||||
// for (channel, state) in states.iter().enumerate() {
|
for (channel, state) in channel_states.iter().enumerate() {
|
||||||
// let _ = writeln!(
|
let _ = writeln!(
|
||||||
// socket, "channel {}: Steinhart-Hart equation parameters",
|
socket, "channel {}: Steinhart-Hart equation parameters",
|
||||||
// channel,
|
channel,
|
||||||
// );
|
);
|
||||||
// let _ = writeln!(socket, "- a={}", state.sh.a);
|
let _ = writeln!(socket, "- t0={}", state.sh.t0);
|
||||||
// let _ = writeln!(socket, "- b={}", state.sh.b);
|
let _ = writeln!(socket, "- r={}", state.sh.r);
|
||||||
// let _ = writeln!(socket, "- c={}", state.sh.c);
|
let _ = writeln!(socket, "- r0={}", state.sh.r0);
|
||||||
// let _ = writeln!(socket, "- parallel_r={}", state.sh.parallel_r);
|
let _ = writeln!(socket, "");
|
||||||
// let _ = writeln!(socket, "");
|
}
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
Command::Show(ShowCommand::PostFilter) => {
|
Command::Show(ShowCommand::PostFilter) => {
|
||||||
// for (channel, _) in states.iter().enumerate() {
|
for (channel, _) in channel_states.iter().enumerate() {
|
||||||
// match adc.get_postfilter(channel as u8).unwrap() {
|
match adc.get_postfilter(channel as u8).unwrap() {
|
||||||
// Some(filter) => {
|
Some(filter) => {
|
||||||
// let _ = writeln!(
|
let _ = writeln!(
|
||||||
// socket, "channel {}: postfilter={:.2} SPS",
|
socket, "channel {}: postfilter={:.2} SPS",
|
||||||
// channel, filter.output_rate().unwrap()
|
channel, filter.output_rate().unwrap()
|
||||||
// );
|
);
|
||||||
// }
|
|
||||||
// None => {
|
|
||||||
// let _ = writeln!(
|
|
||||||
// socket, "channel {}: no postfilter",
|
|
||||||
// channel
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
Command::Pwm { channel, setup: PwmSetup::ISet(PwmMode::Pid) } => {
|
None => {
|
||||||
// states[channel].pid_enabled = true;
|
let _ = writeln!(
|
||||||
// let _ = writeln!(socket, "channel {}: PID enabled to control PWM", channel
|
socket, "channel {}: no postfilter",
|
||||||
// );
|
channel
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Command::Pwm { channel, setup: PwmSetup::ISet(PwmMode::Manual(config))} => {
|
|
||||||
// states[channel].pid_enabled = false;
|
|
||||||
// let PwmConfig { width, total } = config;
|
|
||||||
// match channel {
|
|
||||||
// 0 => tec0.set(TecPin::ISet, width, total),
|
|
||||||
// 1 => tec1.set(TecPin::ISet, width, total),
|
|
||||||
// _ => unreachable!(),
|
|
||||||
// }
|
|
||||||
// let _ = writeln!(
|
|
||||||
// socket, "channel {}: PWM duty cycle manually set to {}/{}",
|
|
||||||
// channel, config.width, config.total
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
Command::Pwm { channel, setup } => {
|
}
|
||||||
// let (pin, config) = match setup {
|
}
|
||||||
// PwmSetup::ISet(_) =>
|
Command::PwmPid { channel } => {
|
||||||
// // Handled above
|
channel_states[channel].pid_enabled = true;
|
||||||
// unreachable!(),
|
let _ = writeln!(socket, "channel {}: PID enabled to control PWM", channel
|
||||||
// PwmSetup::MaxIPos(config) =>
|
);
|
||||||
// (TecPin::MaxIPos, config),
|
}
|
||||||
// PwmSetup::MaxINeg(config) =>
|
Command::Pwm { channel, pin: PwmPin::ISet, duty } if duty <= ad5680::MAX_VALUE => {
|
||||||
// (TecPin::MaxINeg, config),
|
channel_states[channel].pid_enabled = false;
|
||||||
// PwmSetup::MaxV(config) =>
|
match channel {
|
||||||
// (TecPin::MaxV, config),
|
0 => dac0.set(duty).unwrap(),
|
||||||
// };
|
1 => dac1.set(duty).unwrap(),
|
||||||
// let PwmConfig { width, total } = config;
|
_ => unreachable!(),
|
||||||
// match channel {
|
}
|
||||||
// 0 => tec0.set(pin, width, total),
|
channel_states[channel].dac_value = duty;
|
||||||
// 1 => tec1.set(pin, width, total),
|
let _ = writeln!(
|
||||||
// _ => unreachable!(),
|
socket, "channel {}: PWM duty cycle manually set to {}/{}",
|
||||||
// }
|
channel, duty, ad5680::MAX_VALUE
|
||||||
// let _ = writeln!(
|
);
|
||||||
// socket, "channel {}: PWM {} reconfigured to {}/{}",
|
}
|
||||||
// channel, pin, width, total
|
Command::Pwm { pin: PwmPin::ISet, duty, .. } if duty > ad5680::MAX_VALUE => {
|
||||||
// );
|
let _ = writeln!(
|
||||||
|
socket, "error: PWM duty range must not exceed {}",
|
||||||
|
ad5680::MAX_VALUE
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Command::Pwm { channel, pin, duty } if duty <= 0xFFFF => {
|
||||||
|
let duty = duty as u16;
|
||||||
|
|
||||||
|
fn set_pwm_channel<P: hal::PwmPin<Duty=u16>>(pin: &mut P, duty: u16) -> u16 {
|
||||||
|
pin.set_duty(duty);
|
||||||
|
pin.get_max_duty()
|
||||||
|
}
|
||||||
|
let max = match (channel, pin) {
|
||||||
|
(_, PwmPin::ISet) =>
|
||||||
|
// Handled above
|
||||||
|
unreachable!(),
|
||||||
|
(0, PwmPin::MaxIPos) =>
|
||||||
|
set_pwm_channel(&mut pwm.max_i_pos0, duty),
|
||||||
|
(0, PwmPin::MaxINeg) =>
|
||||||
|
set_pwm_channel(&mut pwm.max_i_neg0, duty),
|
||||||
|
(0, PwmPin::MaxV) =>
|
||||||
|
set_pwm_channel(&mut pwm.max_v0, duty),
|
||||||
|
(1, PwmPin::MaxIPos) =>
|
||||||
|
set_pwm_channel(&mut pwm.max_i_pos1, duty),
|
||||||
|
(1, PwmPin::MaxINeg) =>
|
||||||
|
set_pwm_channel(&mut pwm.max_i_neg1, duty),
|
||||||
|
(1, PwmPin::MaxV) =>
|
||||||
|
set_pwm_channel(&mut pwm.max_v1, duty),
|
||||||
|
_ =>
|
||||||
|
unreachable!(),
|
||||||
|
};
|
||||||
|
let _ = writeln!(
|
||||||
|
socket, "channel {}: PWM {} reconfigured to {}/{}",
|
||||||
|
channel, pin.name(), duty, max
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Command::Pwm { duty, .. } if duty > 0xFFFF => {
|
||||||
|
let _ = writeln!(socket, "error: PWM duty range must fit 16 bits");
|
||||||
}
|
}
|
||||||
Command::Pid { channel, parameter, value } => {
|
Command::Pid { channel, parameter, value } => {
|
||||||
// let pid = &mut states[channel].pid;
|
let pid = &mut channel_states[channel].pid;
|
||||||
// use command_parser::PidParameter::*;
|
use command_parser::PidParameter::*;
|
||||||
// match parameter {
|
match parameter {
|
||||||
// Target =>
|
Target =>
|
||||||
// pid.set_target(value),
|
pid.set_target(value),
|
||||||
// KP =>
|
KP =>
|
||||||
// pid.update_parameters(|parameters| parameters.kp = value),
|
pid.update_parameters(|parameters| parameters.kp = value),
|
||||||
// KI =>
|
KI =>
|
||||||
// pid.update_parameters(|parameters| parameters.ki = value),
|
pid.update_parameters(|parameters| parameters.ki = value),
|
||||||
// KD =>
|
KD =>
|
||||||
// pid.update_parameters(|parameters| parameters.kd = value),
|
pid.update_parameters(|parameters| parameters.kd = value),
|
||||||
// OutputMin =>
|
OutputMin =>
|
||||||
// pid.update_parameters(|parameters| parameters.output_min = value),
|
pid.update_parameters(|parameters| parameters.output_min = value),
|
||||||
// OutputMax =>
|
OutputMax =>
|
||||||
// pid.update_parameters(|parameters| parameters.output_max = value),
|
pid.update_parameters(|parameters| parameters.output_max = value),
|
||||||
// IntegralMin =>
|
IntegralMin =>
|
||||||
// pid.update_parameters(|parameters| parameters.integral_min = value),
|
pid.update_parameters(|parameters| parameters.integral_min = value),
|
||||||
// IntegralMax =>
|
IntegralMax =>
|
||||||
// pid.update_parameters(|parameters| parameters.integral_max = value),
|
pid.update_parameters(|parameters| parameters.integral_max = value),
|
||||||
// }
|
}
|
||||||
// pid.reset();
|
pid.reset();
|
||||||
// let _ = writeln!(socket, "PID parameter updated");
|
let _ = writeln!(socket, "PID parameter updated");
|
||||||
}
|
}
|
||||||
Command::SteinhartHart { channel, parameter, value } => {
|
Command::SteinhartHart { channel, parameter, value } => {
|
||||||
// let sh = &mut states[channel].sh;
|
let sh = &mut channel_states[channel].sh;
|
||||||
// use command_parser::ShParameter::*;
|
use command_parser::ShParameter::*;
|
||||||
// match parameter {
|
match parameter {
|
||||||
// A => sh.a = value,
|
T0 => sh.t0 = value,
|
||||||
// B => sh.b = value,
|
R => sh.r = value,
|
||||||
// C => sh.c = value,
|
R0 => sh.r0 = value,
|
||||||
// ParallelR => sh.parallel_r = value,
|
}
|
||||||
// }
|
sh.update();
|
||||||
// let _ = writeln!(socket, "Steinhart-Hart equation parameter updated");
|
let _ = writeln!(socket, "Steinhart-Hart equation parameter updated");
|
||||||
}
|
}
|
||||||
Command::PostFilter { channel, rate } => {
|
Command::PostFilter { channel, rate } => {
|
||||||
// let filter = ad7172::PostFilter::closest(rate);
|
let filter = ad7172::PostFilter::closest(rate);
|
||||||
// match filter {
|
match filter {
|
||||||
// Some(filter) => {
|
Some(filter) => {
|
||||||
// adc.set_postfilter(channel as u8, Some(filter)).unwrap();
|
adc.set_postfilter(channel as u8, Some(filter)).unwrap();
|
||||||
// let _ = writeln!(
|
let _ = writeln!(
|
||||||
// socket, "channel {}: postfilter set to {:.2} SPS",
|
socket, "channel {}: postfilter set to {:.2} SPS",
|
||||||
// channel, filter.output_rate().unwrap()
|
channel, filter.output_rate().unwrap()
|
||||||
// );
|
);
|
||||||
// }
|
}
|
||||||
// None => {
|
None => {
|
||||||
// let _ = writeln!(socket, "Unable to choose postfilter");
|
let _ = writeln!(socket, "Unable to choose postfilter");
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
cmd => {
|
||||||
|
let _ = writeln!(socket, "Not yet implemented: {:?}", cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(SessionOutput::Error(e)) => {
|
Ok(SessionOutput::Error(e)) => {
|
||||||
let _ = writeln!(socket, "Command error: {:?}", e);
|
let _ = writeln!(socket, "Command error: {:?}", e);
|
||||||
}
|
}
|
||||||
|
Ok(o) => {
|
||||||
|
let _ = writeln!(socket, "Not yet implemented");
|
||||||
|
}
|
||||||
Err(_) =>
|
Err(_) =>
|
||||||
socket.close(),
|
socket.close(),
|
||||||
}
|
}
|
||||||
|
|
14
src/pid.rs
14
src/pid.rs
|
@ -9,6 +9,20 @@ pub struct Parameters {
|
||||||
pub integral_max: f64
|
pub integral_max: f64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Parameters {
|
||||||
|
fn default() -> Self {
|
||||||
|
Parameters {
|
||||||
|
kp: 0.5,
|
||||||
|
ki: 0.05,
|
||||||
|
kd: 0.45,
|
||||||
|
output_min: 0.0,
|
||||||
|
output_max: 1.0,
|
||||||
|
integral_min: 0.0,
|
||||||
|
integral_max: 1.0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Controller {
|
pub struct Controller {
|
||||||
parameters: Parameters,
|
parameters: Parameters,
|
||||||
|
|
12
src/pins.rs
12
src/pins.rs
|
@ -171,12 +171,12 @@ impl Pins {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PwmPins {
|
pub struct PwmPins {
|
||||||
max_v0: PwmChannels<TIM3, pwm::C1>,
|
pub max_v0: PwmChannels<TIM3, pwm::C1>,
|
||||||
max_v1: PwmChannels<TIM3, pwm::C2>,
|
pub max_v1: PwmChannels<TIM3, pwm::C2>,
|
||||||
max_i_pos0: PwmChannels<TIM1, pwm::C1>,
|
pub max_i_pos0: PwmChannels<TIM1, pwm::C1>,
|
||||||
max_i_pos1: PwmChannels<TIM1, pwm::C2>,
|
pub max_i_pos1: PwmChannels<TIM1, pwm::C2>,
|
||||||
max_i_neg0: PwmChannels<TIM1, pwm::C3>,
|
pub max_i_neg0: PwmChannels<TIM1, pwm::C3>,
|
||||||
max_i_neg1: PwmChannels<TIM1, pwm::C4>,
|
pub max_i_neg1: PwmChannels<TIM1, pwm::C4>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PwmPins {
|
impl PwmPins {
|
||||||
|
|
Loading…
Reference in New Issue