From 18e3e95615a79fe5534dfcdda90aeb85f7bf6c2a Mon Sep 17 00:00:00 2001 From: Astro Date: Tue, 24 Sep 2019 01:32:34 +0200 Subject: [PATCH] group 4 PWM channels into Tec, add commands to configure them --- firmware/README.md | 37 ++++++----- firmware/src/board/pwm.rs | 4 +- firmware/src/command_parser.rs | 115 ++++++++++++++++++++++++++++----- firmware/src/main.rs | 91 +++++++++++++------------- firmware/src/tec.rs | 65 +++++++++++++++++-- 5 files changed, 227 insertions(+), 85 deletions(-) diff --git a/firmware/README.md b/firmware/README.md index 52a28e2..8496fad 100644 --- a/firmware/README.md +++ b/firmware/README.md @@ -50,20 +50,23 @@ The scope of this setting is per TCP session. ### Commands -| Syntax | Function | -| --- | --- | -| `report` | Show current input | -| `report mode` | Show current report mode | -| `report mode ` | Set report mode | -| `pwm <0/1> ` | Set PWM duty cycle to manual *width / total* | -| `pwm <0/1> pid` | Set PWM to be controlled by PID | -| `pid` | Show PID configuration | -| `pid <0/1> target ` | | -| `pid <0/1> kp ` | | -| `pid <0/1> ki ` | | -| `pid <0/1> kd ` | | -| `pid <0/1> output_min ` | | -| `pid <0/1> output_max ` | | -| `pid <0/1> integral_min ` | | -| `pid <0/1> integral_max ` | | -| `postfilter <0/1> rate ` | Set postfilter output data rate | +| Syntax | Function | +| --- | --- | +| `report` | Show current input | +| `report mode` | Show current report mode | +| `report mode ` | Set report mode | +| `pwm <0/1> max_i_pos ` | Set PWM duty cycle for **max_i_pos** to *width / total* | +| `pwm <0/1> max_i_neg ` | Set PWM duty cycle for **max_i_neg** to *width / total* | +| `pwm <0/1> max_v ` | Set PWM duty cycle for **max_v** to *width / total* | +| `pwm <0/1> ` | Set PWM duty cycle for **i_set** to manual *width / total* | +| `pwm <0/1> pid` | Set PWM to be controlled by PID | +| `pid` | Show PID configuration | +| `pid <0/1> target ` | | +| `pid <0/1> kp ` | | +| `pid <0/1> ki ` | | +| `pid <0/1> kd ` | | +| `pid <0/1> output_min ` | | +| `pid <0/1> output_max ` | | +| `pid <0/1> integral_min ` | | +| `pid <0/1> integral_max ` | | +| `postfilter <0/1> rate ` | Set postfilter output data rate | diff --git a/firmware/src/board/pwm.rs b/firmware/src/board/pwm.rs index 003eebf..0cbdd46 100644 --- a/firmware/src/board/pwm.rs +++ b/firmware/src/board/pwm.rs @@ -14,7 +14,7 @@ pub struct T5CCP1; pub trait PwmPeripheral { type ChannelA: PwmChannel; type ChannelB: PwmChannel; - fn split_16bit_ab() -> (Self::ChannelA, Self::ChannelB); + fn split() -> (Self::ChannelA, Self::ChannelB); } macro_rules! pwm_peripheral { @@ -22,7 +22,7 @@ macro_rules! pwm_peripheral { impl PwmPeripheral for $TIMER { type ChannelA = $A; type ChannelB = $B; - fn split_16bit_ab() -> (Self::ChannelA, Self::ChannelB) { + fn split() -> (Self::ChannelA, Self::ChannelB) { let regs = unsafe { &*Self::ptr() }; regs.cfg.write(|w| unsafe { w.bits(4) }); diff --git a/firmware/src/command_parser.rs b/firmware/src/command_parser.rs index 6d67939..0514f72 100644 --- a/firmware/src/command_parser.rs +++ b/firmware/src/command_parser.rs @@ -81,15 +81,26 @@ pub enum PidParameter { IntegralMax, } +#[derive(Debug, Clone, PartialEq)] +pub struct PwmConfig { + pub width: u16, + pub total: u16, +} + #[derive(Debug, Clone, PartialEq)] pub enum PwmMode { - Manual { - width: u16, - total: u16, - }, + Manual(PwmConfig), Pid, } +#[derive(Debug, Clone, PartialEq)] +pub enum PwmSetup { + ISet(PwmMode), + MaxIPos(PwmConfig), + MaxINeg(PwmConfig), + MaxV(PwmConfig), +} + #[derive(Debug, Clone, PartialEq)] pub enum Command { Quit, @@ -97,7 +108,7 @@ pub enum Command { Reporting(bool), Pwm { channel: usize, - mode: PwmMode, + setup: PwmSetup, }, Pid { channel: usize, @@ -176,8 +187,8 @@ fn report(input: &[u8]) -> IResult<&[u8], Command> { )(input) } -/// `pwm <0-1> ` - Set pwm duty cycle -fn pwm_manual(input: &[u8]) -> IResult<&[u8], Result> { +/// `pwm ... ` - Set pwm duty cycle +fn pwm_config(input: &[u8]) -> IResult<&[u8], Result> { let (input, width) = unsigned(input)?; let width = match width { Ok(width) => width, @@ -189,12 +200,50 @@ fn pwm_manual(input: &[u8]) -> IResult<&[u8], Result> { Ok(total) => total, Err(e) => return Ok((input, Err(e.into()))), }; - Ok((input, Ok(PwmMode::Manual { width, total }))) + Ok((input, Ok(PwmConfig { width, total }))) +} + +fn pwm_setup(input: &[u8]) -> IResult<&[u8], Result> { + alt(( + map( + preceded( + tag("max_i_pos"), + preceded( + whitespace, + pwm_config + ) + ), + |result| result.map(PwmSetup::MaxIPos) + ), + map( + preceded( + tag("max_i_neg"), + preceded( + whitespace, + pwm_config + ) + ), + |result| result.map(PwmSetup::MaxINeg) + ), + map( + preceded( + tag("max_v"), + preceded( + whitespace, + pwm_config + ) + ), + |result| result.map(PwmSetup::MaxV) + ), + map(pwm_config, |result| result.map(|config| { + PwmSetup::ISet(PwmMode::Manual(config)) + })) + ))(input) } /// `pwm <0-1> pid` - Set PWM to be controlled by PID -fn pwm_pid(input: &[u8]) -> IResult<&[u8], Result> { - value(Ok(PwmMode::Pid), tag("pid"))(input) +fn pwm_pid(input: &[u8]) -> IResult<&[u8], Result> { + value(Ok(PwmSetup::ISet(PwmMode::Pid)), tag("pid"))(input) } fn pwm(input: &[u8]) -> IResult<&[u8], Result> { @@ -208,10 +257,10 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result> { whitespace, alt(( pwm_pid, - pwm_manual, + pwm_setup )) ), - |(channel, mode)| mode.map(|mode| Command::Pwm { channel, mode }) + |(channel, setup)| setup.map(|setup| Command::Pwm { channel, setup }) ) ), value(Ok(Command::Show(ShowCommand::Pwm)), end) @@ -334,10 +383,10 @@ mod test { let command = Command::parse(b"pwm 1 16383 65535"); assert_eq!(command, Ok(Command::Pwm { channel: 1, - mode: PwmMode::Manual { + setup: PwmSetup::ISet(PwmMode::Manual(PwmConfig { width: 16383, total: 65535, - }, + })), })); } @@ -346,7 +395,43 @@ mod test { let command = Command::parse(b"pwm 0 pid"); assert_eq!(command, Ok(Command::Pwm { channel: 0, - mode: PwmMode::Pid, + setup: PwmSetup::ISet(PwmMode::Pid), + })); + } + + #[test] + fn parse_pwm_max_i_pos() { + let command = Command::parse(b"pwm 0 max_i_pos 7 13"); + assert_eq!(command, Ok(Command::Pwm { + channel: 0, + setup: PwmSetup::MaxIPos(PwmConfig { + width: 7, + total: 13, + }), + })); + } + + #[test] + fn parse_pwm_max_i_neg() { + let command = Command::parse(b"pwm 0 max_i_neg 128 65535"); + assert_eq!(command, Ok(Command::Pwm { + channel: 0, + setup: PwmSetup::MaxINeg(PwmConfig { + width: 128, + total: 65535, + }), + })); + } + + #[test] + fn parse_pwm_max_v() { + let command = Command::parse(b"pwm 0 max_v 32768 65535"); + assert_eq!(command, Ok(Command::Pwm { + channel: 0, + setup: PwmSetup::MaxV(PwmConfig { + width: 32768, + total: 65535, + }), })); } diff --git a/firmware/src/main.rs b/firmware/src/main.rs index 73dbf2a..c82d555 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -38,17 +38,16 @@ mod board; use self::board::{ gpio::Gpio, systick::get_time, - pwm::*, }; mod ethmac; mod command_parser; -use command_parser::{Command, ShowCommand, PwmMode}; +use command_parser::{Command, ShowCommand, PwmSetup, PwmMode, PwmConfig}; mod session; use self::session::{Session, SessionOutput}; mod ad7172; mod pid; mod tec; -use tec::TEC; +use tec::{Tec, TecPin}; pub struct UART0; @@ -83,29 +82,6 @@ macro_rules! create_socket { ) } -fn init_pwm_tec() -> ( - TEC, - TEC -) { - let (t2ccp0, t2ccp1) = tm4c129x::TIMER2::split_16bit_ab(); - let (t3ccp0, t3ccp1) = tm4c129x::TIMER3::split_16bit_ab(); - let (t4ccp0, t4ccp1) = tm4c129x::TIMER4::split_16bit_ab(); - let (t5ccp0, t5ccp1) = tm4c129x::TIMER5::split_16bit_ab(); - let tec0 = TEC { - max_i_pos: t2ccp0, - max_i_neg: t2ccp1, - i_set: t3ccp0, - max_v: t3ccp1, - }; - let tec1 = TEC { - max_i_pos: t4ccp0, - max_i_neg: t4ccp1, - i_set: t5ccp0, - max_v: t5ccp1, - }; - (tec0, tec1) -} - const DEFAULT_PID_PARAMETERS: pid::Parameters = pid::Parameters { kp: 1.0, ki: 1.0, @@ -116,9 +92,14 @@ const DEFAULT_PID_PARAMETERS: pid::Parameters = pid::Parameters { integral_max: 0xffff as f32, }; +const PWM_PID_WIDTH: u16 = 0xffff; + +// TODO: maybe rename to `TECS`? +/// Number of TEC channels with four PWM channels each pub const CHANNELS: usize = 2; -/// State per channel +// TODO: maybe rename to `TecState`? +/// State per TEC channel #[derive(Clone)] struct ControlState { /// Report data (time, data) @@ -131,20 +112,21 @@ struct ControlState { #[entry] fn main() -> ! { let mut stdout = hio::hstdout().unwrap(); - writeln!(stdout, "ionpak boot").unwrap(); + writeln!(stdout, "tecpak boot").unwrap(); board::init(); - let (mut tec0, mut tec1) = init_pwm_tec(); writeln!(stdout, "board initialized").unwrap(); + let mut tec0 = Tec::tec0(); + let mut tec1 = Tec::tec1(); println!(r#" _ _ - (_) | | - _ ___ _ __ _ __ __ _| | - | |/ _ \| '_ \| '_ \ / _` | |/ / - | | (_) | | | | |_) | (_| | < - |_|\___/|_| |_| .__/ \__,_|_|\_\ + | | | | +/ _/___ _ __ _ __ __ _| | + | |/ _ \ /'__\| '_ \ / _` | |/ / + | | (/_/| |___| |_) | (_| | < + |_|\___\ \___/| .__/ \__,_|_|\_\ | | - |_| + |_| v1 "#); // CSn let pb4 = board::gpio::PB4.into_output(); @@ -267,8 +249,8 @@ fn main() -> ! { if state.pid_enabled { let width = state.pid.update(data as f32) as u16; match channel { - 0 => tec0.i_set.set(width, 0xffff), - 1 => tec1.i_set.set(width, 0xffff), + 0 => tec0.set(TecPin::ISet, width, PWM_PID_WIDTH), + 1 => tec1.set(TecPin::ISet, width, PWM_PID_WIDTH), _ => unreachable!(), } } @@ -332,6 +314,7 @@ fn main() -> ! { } } Command::Show(ShowCommand::Pwm) => { + // TODO: show all pwms, show actual state for (channel, state) in states.iter().enumerate() { let _ = writeln!( socket, "PWM {}: PID {}", @@ -358,21 +341,41 @@ fn main() -> ! { } } } - Command::Pwm { channel, mode: PwmMode::Manual { width, total }} => { + Command::Pwm { channel, setup: PwmSetup::ISet(PwmMode::Pid) } => { + states[channel].pid_enabled = true; + let _ = writeln!(socket, "channel {}: PID enabled to control PWM", channel); + } + Command::Pwm { channel, setup: PwmSetup::ISet(PwmMode::Manual(config))} => { states[channel].pid_enabled = false; + let PwmConfig { width, total } = config; match channel { - 0 => tec0.i_set.set(width, total), - 1 => tec1.i_set.set(width, total), + 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, width, total + channel, config.width, config.total ); } - Command::Pwm { channel, mode: PwmMode::Pid } => { - states[channel].pid_enabled = true; - let _ = writeln!(socket, "channel {}: PID enabled to control PWM", channel); + Command::Pwm { channel, setup } => { + let (pin, config) = match setup { + PwmSetup::ISet(_) => + // Handled above + unreachable!(), + PwmSetup::MaxIPos(config) => + (TecPin::MaxIPos, config), + PwmSetup::MaxINeg(config) => + (TecPin::MaxINeg, config), + PwmSetup::MaxV(config) => + (TecPin::MaxV, config), + }; + let PwmConfig { width, total } = config; + match channel { + 0 => tec0.set(pin, width, total), + 1 => tec1.set(pin, width, total), + _ => unreachable!(), + } } Command::Pid { channel, parameter, value } => { let pid = &mut states[channel].pid; diff --git a/firmware/src/tec.rs b/firmware/src/tec.rs index ebb6dda..d2c1193 100644 --- a/firmware/src/tec.rs +++ b/firmware/src/tec.rs @@ -1,9 +1,60 @@ -use crate::board::pwm::PwmChannel; +use crate::board::pwm::{self, PwmChannel, PwmPeripheral}; -/// Thermo-Electric Cooling device controlled through PWM -pub struct TEC { - pub max_i_pos: MaxIPos, - pub max_i_neg: MaxINeg, - pub i_set: ISet, - pub max_v: MaxV, +#[derive(Clone, Copy, Debug)] +pub enum TecPin { + ISet, + MaxIPos, + MaxINeg, + MaxV, +} + +/// Thermo-Electric Cooling device controlled through four PWM +/// channels +pub struct Tec { + max_i_pos: MaxIPos, + max_i_neg: MaxINeg, + i_set: ISet, + max_v: MaxV, +} + +impl Tec { + pub fn tec0() -> Self { + let (t2ccp0, t2ccp1) = tm4c129x::TIMER2::split(); + let (t3ccp0, t3ccp1) = tm4c129x::TIMER3::split(); + Tec { + max_i_pos: t2ccp0, + max_i_neg: t2ccp1, + i_set: t3ccp0, + max_v: t3ccp1, + } + } +} + +impl Tec { + pub fn tec1() -> Self { + let (t4ccp0, t4ccp1) = tm4c129x::TIMER4::split(); + let (t5ccp0, t5ccp1) = tm4c129x::TIMER5::split(); + Tec { + max_i_pos: t4ccp0, + max_i_neg: t4ccp1, + i_set: t5ccp0, + max_v: t5ccp1, + } + } +} + + +impl Tec { + pub fn set(&mut self, pin: TecPin, width: u16, total: u16) { + match pin { + TecPin::MaxIPos => + self.max_i_pos.set(width, total), + TecPin::MaxINeg => + self.max_i_neg.set(width, total), + TecPin::ISet => + self.i_set.set(width, total), + TecPin::MaxV => + self.max_v.set(width, total), + } + } }