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

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

View File

@ -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 <off/on>` | Set report mode |
| `pwm <0/1> <width> <total>` | 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 <value>` | |
| `pid <0/1> kp <value>` | |
| `pid <0/1> ki <value>` | |
| `pid <0/1> kd <value>` | |
| `pid <0/1> output_min <value>` | |
| `pid <0/1> output_max <value>` | |
| `pid <0/1> integral_min <value>` | |
| `pid <0/1> integral_max <value>` | |
| `postfilter <0/1> rate <rate>` | Set postfilter output data rate |
| Syntax | Function |
| --- | --- |
| `report` | Show current input |
| `report mode` | Show current report mode |
| `report mode <off/on>` | Set report mode |
| `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 |
| `pid` | Show PID configuration |
| `pid <0/1> target <value>` | |
| `pid <0/1> kp <value>` | |
| `pid <0/1> ki <value>` | |
| `pid <0/1> kd <value>` | |
| `pid <0/1> output_min <value>` | |
| `pid <0/1> output_max <value>` | |
| `pid <0/1> integral_min <value>` | |
| `pid <0/1> integral_max <value>` | |
| `postfilter <0/1> rate <rate>` | Set postfilter output data rate |

View File

@ -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) });

View File

@ -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> <width> <total>` - Set pwm duty cycle
fn pwm_manual(input: &[u8]) -> IResult<&[u8], Result<PwmMode, Error>> {
/// `pwm ... <width> <total>` - Set pwm duty cycle
fn pwm_config(input: &[u8]) -> IResult<&[u8], Result<PwmConfig, Error>> {
let (input, width) = unsigned(input)?;
let width = match width {
Ok(width) => width,
@ -189,12 +200,50 @@ fn pwm_manual(input: &[u8]) -> IResult<&[u8], Result<PwmMode, Error>> {
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<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
fn pwm_pid(input: &[u8]) -> IResult<&[u8], Result<PwmMode, Error>> {
value(Ok(PwmMode::Pid), tag("pid"))(input)
fn pwm_pid(input: &[u8]) -> IResult<&[u8], Result<PwmSetup, Error>> {
value(Ok(PwmSetup::ISet(PwmMode::Pid)), tag("pid"))(input)
}
fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
@ -208,10 +257,10 @@ fn pwm(input: &[u8]) -> IResult<&[u8], Result<Command, Error>> {
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,
}),
}));
}

View File

@ -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<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 {
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;

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
pub struct TEC<MaxIPos: PwmChannel, MaxINeg: PwmChannel, ISet: PwmChannel, MaxV: PwmChannel> {
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<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),
}
}
}