group 4 PWM channels into Tec, add commands to configure them
This commit is contained in:
parent
a82ffadb35
commit
18e3e95615
@ -55,7 +55,10 @@ The scope of this setting is per TCP session.
|
||||
| `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> 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>` | |
|
||||
|
@ -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) });
|
||||
|
||||
|
@ -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,
|
||||
}),
|
||||
}));
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user