group 4 PWM channels into Tec, add commands to configure them

This commit is contained in:
Astro 2019-09-24 01:32:34 +02:00
parent a82ffadb35
commit 18e3e95615
5 changed files with 227 additions and 85 deletions

View File

@ -55,7 +55,10 @@ The scope of this setting is per TCP session.
| `report` | Show current input | | `report` | Show current input |
| `report mode` | Show current report mode | | `report mode` | Show current report mode |
| `report mode <off/on>` | Set report mode | | `report mode <off/on>` | Set report mode |
| `pwm <0/1> <width> <total>` | Set PWM duty cycle to manual *width / total* | | `pwm <0/1> max_i_pos <width> <total>` | Set PWM duty cycle for **max_i_pos** to *width / total* |
| `pwm <0/1> max_i_neg <width> <total>` | Set PWM duty cycle for **max_i_neg** to *width / total* |
| `pwm <0/1> max_v <width> <total>` | Set PWM duty cycle for **max_v** to *width / total* |
| `pwm <0/1> <width> <total>` | Set PWM duty cycle for **i_set** to manual *width / total* |
| `pwm <0/1> pid` | Set PWM to be controlled by PID | | `pwm <0/1> pid` | Set PWM to be controlled by PID |
| `pid` | Show PID configuration | | `pid` | Show PID configuration |
| `pid <0/1> target <value>` | | | `pid <0/1> target <value>` | |

View File

@ -14,7 +14,7 @@ pub struct T5CCP1;
pub trait PwmPeripheral { pub trait PwmPeripheral {
type ChannelA: PwmChannel; type ChannelA: PwmChannel;
type ChannelB: PwmChannel; type ChannelB: PwmChannel;
fn split_16bit_ab() -> (Self::ChannelA, Self::ChannelB); fn split() -> (Self::ChannelA, Self::ChannelB);
} }
macro_rules! pwm_peripheral { macro_rules! pwm_peripheral {
@ -22,7 +22,7 @@ macro_rules! pwm_peripheral {
impl PwmPeripheral for $TIMER { impl PwmPeripheral for $TIMER {
type ChannelA = $A; type ChannelA = $A;
type ChannelB = $B; type ChannelB = $B;
fn split_16bit_ab() -> (Self::ChannelA, Self::ChannelB) { fn split() -> (Self::ChannelA, Self::ChannelB) {
let regs = unsafe { &*Self::ptr() }; let regs = unsafe { &*Self::ptr() };
regs.cfg.write(|w| unsafe { w.bits(4) }); regs.cfg.write(|w| unsafe { w.bits(4) });

View File

@ -81,15 +81,26 @@ pub enum PidParameter {
IntegralMax, IntegralMax,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct PwmConfig {
pub width: u16,
pub total: u16,
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum PwmMode { pub enum PwmMode {
Manual { Manual(PwmConfig),
width: u16,
total: u16,
},
Pid, Pid,
} }
#[derive(Debug, Clone, PartialEq)]
pub enum PwmSetup {
ISet(PwmMode),
MaxIPos(PwmConfig),
MaxINeg(PwmConfig),
MaxV(PwmConfig),
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Command { pub enum Command {
Quit, Quit,
@ -97,7 +108,7 @@ pub enum Command {
Reporting(bool), Reporting(bool),
Pwm { Pwm {
channel: usize, channel: usize,
mode: PwmMode, setup: PwmSetup,
}, },
Pid { Pid {
channel: usize, channel: usize,
@ -176,8 +187,8 @@ fn report(input: &[u8]) -> IResult<&[u8], Command> {
)(input) )(input)
} }
/// `pwm <0-1> <width> <total>` - Set pwm duty cycle /// `pwm ... <width> <total>` - Set pwm duty cycle
fn pwm_manual(input: &[u8]) -> IResult<&[u8], Result<PwmMode, Error>> { fn pwm_config(input: &[u8]) -> IResult<&[u8], Result<PwmConfig, Error>> {
let (input, width) = unsigned(input)?; let (input, width) = unsigned(input)?;
let width = match width { let width = match width {
Ok(width) => width, Ok(width) => width,
@ -189,12 +200,50 @@ fn pwm_manual(input: &[u8]) -> IResult<&[u8], Result<PwmMode, Error>> {
Ok(total) => total, Ok(total) => total,
Err(e) => return Ok((input, Err(e.into()))), 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<PwmSetup, Error>> {
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 /// `pwm <0-1> pid` - Set PWM to be controlled by PID
fn pwm_pid(input: &[u8]) -> IResult<&[u8], Result<PwmMode, Error>> { fn pwm_pid(input: &[u8]) -> IResult<&[u8], Result<PwmSetup, Error>> {
value(Ok(PwmMode::Pid), tag("pid"))(input) value(Ok(PwmSetup::ISet(PwmMode::Pid)), tag("pid"))(input)
} }
fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> { fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
@ -208,10 +257,10 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
whitespace, whitespace,
alt(( alt((
pwm_pid, 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) value(Ok(Command::Show(ShowCommand::Pwm)), end)
@ -334,10 +383,10 @@ mod test {
let command = Command::parse(b"pwm 1 16383 65535"); let command = Command::parse(b"pwm 1 16383 65535");
assert_eq!(command, Ok(Command::Pwm { assert_eq!(command, Ok(Command::Pwm {
channel: 1, channel: 1,
mode: PwmMode::Manual { setup: PwmSetup::ISet(PwmMode::Manual(PwmConfig {
width: 16383, width: 16383,
total: 65535, total: 65535,
}, })),
})); }));
} }
@ -346,7 +395,43 @@ mod test {
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::Pwm {
channel: 0, 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,
}),
})); }));
} }

View File

@ -38,17 +38,16 @@ mod board;
use self::board::{ use self::board::{
gpio::Gpio, gpio::Gpio,
systick::get_time, systick::get_time,
pwm::*,
}; };
mod ethmac; mod ethmac;
mod command_parser; mod command_parser;
use command_parser::{Command, ShowCommand, PwmMode}; use command_parser::{Command, ShowCommand, PwmSetup, PwmMode, PwmConfig};
mod session; mod session;
use self::session::{Session, SessionOutput}; use self::session::{Session, SessionOutput};
mod ad7172; mod ad7172;
mod pid; mod pid;
mod tec; mod tec;
use tec::TEC; use tec::{Tec, TecPin};
pub struct UART0; pub struct UART0;
@ -83,29 +82,6 @@ macro_rules! create_socket {
) )
} }
fn init_pwm_tec() -> (
TEC<T2CCP0, T2CCP1, T3CCP0, T3CCP1>,
TEC<T4CCP0, T4CCP1, T5CCP0, T5CCP1>
) {
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 { const DEFAULT_PID_PARAMETERS: pid::Parameters = pid::Parameters {
kp: 1.0, kp: 1.0,
ki: 1.0, ki: 1.0,
@ -116,9 +92,14 @@ const DEFAULT_PID_PARAMETERS: pid::Parameters = pid::Parameters {
integral_max: 0xffff as f32, 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; pub const CHANNELS: usize = 2;
/// State per channel // TODO: maybe rename to `TecState`?
/// State per TEC channel
#[derive(Clone)] #[derive(Clone)]
struct ControlState { struct ControlState {
/// Report data (time, data) /// Report data (time, data)
@ -131,20 +112,21 @@ struct ControlState {
#[entry] #[entry]
fn main() -> ! { fn main() -> ! {
let mut stdout = hio::hstdout().unwrap(); let mut stdout = hio::hstdout().unwrap();
writeln!(stdout, "ionpak boot").unwrap(); writeln!(stdout, "tecpak boot").unwrap();
board::init(); board::init();
let (mut tec0, mut tec1) = init_pwm_tec();
writeln!(stdout, "board initialized").unwrap(); writeln!(stdout, "board initialized").unwrap();
let mut tec0 = Tec::tec0();
let mut tec1 = Tec::tec1();
println!(r#" println!(r#"
_ _ _ _
(_) | | | | | |
_ ___ _ __ _ __ __ _| | / _/___ _ __ _ __ __ _| |
| |/ _ \| '_ \| '_ \ / _` | |/ / | |/ _ \ /'__\| '_ \ / _` | |/ /
| | (_) | | | | |_) | (_| | < | | (/_/| |___| |_) | (_| | <
|_|\___/|_| |_| .__/ \__,_|_|\_\ |_|\___\ \___/| .__/ \__,_|_|\_\
| | | |
|_| |_| v1
"#); "#);
// CSn // CSn
let pb4 = board::gpio::PB4.into_output(); let pb4 = board::gpio::PB4.into_output();
@ -267,8 +249,8 @@ fn main() -> ! {
if state.pid_enabled { if state.pid_enabled {
let width = state.pid.update(data as f32) as u16; let width = state.pid.update(data as f32) as u16;
match channel { match channel {
0 => tec0.i_set.set(width, 0xffff), 0 => tec0.set(TecPin::ISet, width, PWM_PID_WIDTH),
1 => tec1.i_set.set(width, 0xffff), 1 => tec1.set(TecPin::ISet, width, PWM_PID_WIDTH),
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -332,6 +314,7 @@ fn main() -> ! {
} }
} }
Command::Show(ShowCommand::Pwm) => { Command::Show(ShowCommand::Pwm) => {
// TODO: show all pwms, show actual state
for (channel, state) in states.iter().enumerate() { for (channel, state) in states.iter().enumerate() {
let _ = writeln!( let _ = writeln!(
socket, "PWM {}: PID {}", 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; states[channel].pid_enabled = false;
let PwmConfig { width, total } = config;
match channel { match channel {
0 => tec0.i_set.set(width, total), 0 => tec0.set(TecPin::ISet, width, total),
1 => tec1.i_set.set(width, total), 1 => tec1.set(TecPin::ISet, width, total),
_ => unreachable!(), _ => unreachable!(),
} }
let _ = writeln!( let _ = writeln!(
socket, "channel {}: PWM duty cycle manually set to {}/{}", socket, "channel {}: PWM duty cycle manually set to {}/{}",
channel, width, total channel, config.width, config.total
); );
} }
Command::Pwm { channel, mode: PwmMode::Pid } => { Command::Pwm { channel, setup } => {
states[channel].pid_enabled = true; let (pin, config) = match setup {
let _ = writeln!(socket, "channel {}: PID enabled to control PWM", channel); 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 } => { Command::Pid { channel, parameter, value } => {
let pid = &mut states[channel].pid; let pid = &mut states[channel].pid;

View File

@ -1,9 +1,60 @@
use crate::board::pwm::PwmChannel; use crate::board::pwm::{self, PwmChannel, PwmPeripheral};
/// Thermo-Electric Cooling device controlled through PWM #[derive(Clone, Copy, Debug)]
pub struct TEC<MaxIPos: PwmChannel, MaxINeg: PwmChannel, ISet: PwmChannel, MaxV: PwmChannel> { pub enum TecPin {
pub max_i_pos: MaxIPos, ISet,
pub max_i_neg: MaxINeg, MaxIPos,
pub i_set: ISet, MaxINeg,
pub max_v: MaxV, MaxV,
}
/// Thermo-Electric Cooling device controlled through four PWM
/// channels
pub struct Tec<MaxIPos: PwmChannel, MaxINeg: PwmChannel, ISet: PwmChannel, MaxV: PwmChannel> {
max_i_pos: MaxIPos,
max_i_neg: MaxINeg,
i_set: ISet,
max_v: MaxV,
}
impl Tec<pwm::T2CCP0, pwm::T2CCP1, pwm::T3CCP0, pwm::T3CCP1> {
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<pwm::T4CCP0, pwm::T4CCP1, pwm::T5CCP0, pwm::T5CCP1> {
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<MaxIPos: PwmChannel, MaxINeg: PwmChannel, ISet: PwmChannel, MaxV: PwmChannel> Tec<MaxIPos, MaxINeg, ISet, MaxV> {
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),
}
}
} }