forked from M-Labs/ionpak-thermostat
add PWM abstraction for TEC control
This commit is contained in:
parent
8d01ca8d20
commit
a82ffadb35
|
@ -4,6 +4,7 @@ use tm4c129x;
|
|||
pub mod gpio;
|
||||
pub mod softspi;
|
||||
pub mod systick;
|
||||
pub mod pwm;
|
||||
|
||||
|
||||
const UART_DIV: u32 = (((/*sysclk*/120_000_000 * 8) / /*baud*/115200) + 1) / 2;
|
||||
|
@ -130,66 +131,10 @@ pub fn init() {
|
|||
}
|
||||
while !timers_ready(sysctl) {}
|
||||
|
||||
// Manual: 13.4.5 PWM Mode
|
||||
macro_rules! setup_timer_pwm {
|
||||
($T: tt) => (
|
||||
let timer = unsafe { &*tm4c129x::$T::ptr() };
|
||||
timer.cfg.write(|w| unsafe { w.bits(4) });
|
||||
timer.tamr.modify(|_, w| unsafe {
|
||||
w
|
||||
.taams().bit(true)
|
||||
.tacmr().bit(false)
|
||||
.tamr().bits(2)
|
||||
});
|
||||
timer.tbmr.modify(|_, w| unsafe {
|
||||
w
|
||||
.tbams().bit(true)
|
||||
.tbcmr().bit(false)
|
||||
.tbmr().bits(2)
|
||||
});
|
||||
timer.ctl.modify(|_, w| {
|
||||
w
|
||||
.tapwml().bit(false)
|
||||
.tbpwml().bit(false)
|
||||
});
|
||||
// no prescaler
|
||||
// no interrupts
|
||||
timer.tailr.write(|w| unsafe { w.bits(0xFFFF) });
|
||||
timer.tbilr.write(|w| unsafe { w.bits(0xFFFF) });
|
||||
timer.tamatchr.write(|w| unsafe { w.bits(0x8000) });
|
||||
timer.tbmatchr.write(|w| unsafe { w.bits(0x8000) });
|
||||
timer.ctl.modify(|_, w| {
|
||||
w
|
||||
.taen().bit(true)
|
||||
.tben().bit(true)
|
||||
});
|
||||
)
|
||||
}
|
||||
setup_timer_pwm!(TIMER2);
|
||||
setup_timer_pwm!(TIMER3);
|
||||
setup_timer_pwm!(TIMER4);
|
||||
setup_timer_pwm!(TIMER5);
|
||||
|
||||
systick::init(cs);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn set_timer_pwm(matchr: u32, ilr: u32) {
|
||||
macro_rules! set_timer_pwm {
|
||||
($T: tt) => (
|
||||
let timer = unsafe { &*tm4c129x::$T::ptr() };
|
||||
timer.tamatchr.write(|w| unsafe { w.bits(matchr) });
|
||||
timer.tbmatchr.write(|w| unsafe { w.bits(matchr) });
|
||||
timer.tailr.write(|w| unsafe { w.bits(ilr) });
|
||||
timer.tbilr.write(|w| unsafe { w.bits(ilr) });
|
||||
)
|
||||
}
|
||||
set_timer_pwm!(TIMER2);
|
||||
set_timer_pwm!(TIMER3);
|
||||
set_timer_pwm!(TIMER4);
|
||||
set_timer_pwm!(TIMER5);
|
||||
}
|
||||
|
||||
pub fn get_mac_address() -> [u8; 6] {
|
||||
let (userreg0, userreg1) = cortex_m::interrupt::free(|_cs| {
|
||||
let flashctl = unsafe { &*tm4c129x::FLASH_CTRL::ptr() };
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
use tm4c129x::{
|
||||
TIMER2, TIMER3, TIMER4, TIMER5,
|
||||
};
|
||||
|
||||
pub struct T2CCP0;
|
||||
pub struct T2CCP1;
|
||||
pub struct T3CCP0;
|
||||
pub struct T3CCP1;
|
||||
pub struct T4CCP0;
|
||||
pub struct T4CCP1;
|
||||
pub struct T5CCP0;
|
||||
pub struct T5CCP1;
|
||||
|
||||
pub trait PwmPeripheral {
|
||||
type ChannelA: PwmChannel;
|
||||
type ChannelB: PwmChannel;
|
||||
fn split_16bit_ab() -> (Self::ChannelA, Self::ChannelB);
|
||||
}
|
||||
|
||||
macro_rules! pwm_peripheral {
|
||||
($TIMER: ty, $A: tt, $B: tt) => {
|
||||
impl PwmPeripheral for $TIMER {
|
||||
type ChannelA = $A;
|
||||
type ChannelB = $B;
|
||||
fn split_16bit_ab() -> (Self::ChannelA, Self::ChannelB) {
|
||||
let regs = unsafe { &*Self::ptr() };
|
||||
regs.cfg.write(|w| unsafe { w.bits(4) });
|
||||
|
||||
let mut a = $A;
|
||||
a.configure();
|
||||
let mut b = $B;
|
||||
b.configure();
|
||||
(a, b)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pwm_peripheral!(TIMER2, T2CCP0, T2CCP1);
|
||||
pwm_peripheral!(TIMER3, T3CCP0, T3CCP1);
|
||||
pwm_peripheral!(TIMER4, T4CCP0, T4CCP1);
|
||||
pwm_peripheral!(TIMER5, T5CCP0, T5CCP1);
|
||||
|
||||
|
||||
pub trait PwmChannel {
|
||||
fn configure(&mut self);
|
||||
fn set(&mut self, width: u16, total: u16);
|
||||
}
|
||||
|
||||
macro_rules! pwm_channel_a {
|
||||
($CHANNEL: ty, $TIMER: tt) => {
|
||||
impl PwmChannel for $CHANNEL {
|
||||
fn configure(&mut self) {
|
||||
let timer = unsafe { &*tm4c129x::$TIMER::ptr() };
|
||||
timer.tamr.modify(|_, w| unsafe {
|
||||
w
|
||||
.taams().bit(true)
|
||||
.tacmr().bit(false)
|
||||
.tamr().bits(2)
|
||||
});
|
||||
timer.ctl.modify(|_, w| {
|
||||
w
|
||||
.tapwml().bit(false)
|
||||
});
|
||||
// no prescaler
|
||||
// no interrupts
|
||||
timer.tailr.write(|w| unsafe { w.bits(0xFFFF) });
|
||||
timer.tamatchr.write(|w| unsafe { w.bits(0x0) });
|
||||
timer.ctl.modify(|_, w| {
|
||||
w
|
||||
.taen().bit(true)
|
||||
});
|
||||
}
|
||||
|
||||
fn set(&mut self, width: u16, total: u16) {
|
||||
let timer = unsafe { &*tm4c129x::$TIMER::ptr() };
|
||||
timer.tamatchr.write(|w| unsafe { w.bits(width.into()) });
|
||||
timer.tailr.write(|w| unsafe { w.bits(total.into()) });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! pwm_channel_b {
|
||||
($CHANNEL: ty, $TIMER: tt) => {
|
||||
impl PwmChannel for $CHANNEL {
|
||||
fn configure(&mut self) {
|
||||
let timer = unsafe { &*tm4c129x::$TIMER::ptr() };
|
||||
timer.tbmr.modify(|_, w| unsafe {
|
||||
w
|
||||
.tbams().bit(true)
|
||||
.tbcmr().bit(false)
|
||||
.tbmr().bits(2)
|
||||
});
|
||||
timer.ctl.modify(|_, w| {
|
||||
w
|
||||
.tbpwml().bit(false)
|
||||
});
|
||||
// no prescaler
|
||||
// no interrupts
|
||||
timer.tbilr.write(|w| unsafe { w.bits(0xFFFF) });
|
||||
timer.tbmatchr.write(|w| unsafe { w.bits(0x0) });
|
||||
timer.ctl.modify(|_, w| {
|
||||
w
|
||||
.tben().bit(true)
|
||||
});
|
||||
}
|
||||
|
||||
fn set(&mut self, width: u16, total: u16) {
|
||||
let timer = unsafe { &*tm4c129x::$TIMER::ptr() };
|
||||
timer.tbmatchr.write(|w| unsafe { w.bits(width.into()) });
|
||||
timer.tbilr.write(|w| unsafe { w.bits(total.into()) });
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pwm_channel_a!(T2CCP0, TIMER2);
|
||||
pwm_channel_b!(T2CCP1, TIMER2);
|
||||
pwm_channel_a!(T3CCP0, TIMER3);
|
||||
pwm_channel_b!(T3CCP1, TIMER3);
|
||||
pwm_channel_a!(T4CCP0, TIMER4);
|
||||
pwm_channel_b!(T4CCP1, TIMER4);
|
||||
pwm_channel_a!(T5CCP0, TIMER5);
|
||||
pwm_channel_b!(T5CCP1, TIMER5);
|
|
@ -84,8 +84,8 @@ pub enum PidParameter {
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PwmMode {
|
||||
Manual {
|
||||
width: u32,
|
||||
total: u32,
|
||||
width: u16,
|
||||
total: u16,
|
||||
},
|
||||
Pid,
|
||||
}
|
||||
|
@ -123,7 +123,7 @@ fn whitespace(input: &[u8]) -> IResult<&[u8], ()> {
|
|||
fold_many1(char(' '), (), |(), _| ())(input)
|
||||
}
|
||||
|
||||
fn unsigned(input: &[u8]) -> IResult<&[u8], Result<u32, Error>> {
|
||||
fn unsigned(input: &[u8]) -> IResult<&[u8], Result<u16, Error>> {
|
||||
take_while1(is_digit)(input)
|
||||
.map(|(input, digits)| {
|
||||
let result = lexical::parse(digits)
|
||||
|
|
|
@ -35,7 +35,11 @@ pub fn panic_fmt(info: &core::panic::PanicInfo) -> ! {
|
|||
}
|
||||
|
||||
mod board;
|
||||
use self::board::{gpio::Gpio, systick::get_time};
|
||||
use self::board::{
|
||||
gpio::Gpio,
|
||||
systick::get_time,
|
||||
pwm::*,
|
||||
};
|
||||
mod ethmac;
|
||||
mod command_parser;
|
||||
use command_parser::{Command, ShowCommand, PwmMode};
|
||||
|
@ -43,6 +47,8 @@ mod session;
|
|||
use self::session::{Session, SessionOutput};
|
||||
mod ad7172;
|
||||
mod pid;
|
||||
mod tec;
|
||||
use tec::TEC;
|
||||
|
||||
pub struct UART0;
|
||||
|
||||
|
@ -77,6 +83,29 @@ 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,
|
||||
|
@ -104,6 +133,7 @@ fn main() -> ! {
|
|||
let mut stdout = hio::hstdout().unwrap();
|
||||
writeln!(stdout, "ionpak boot").unwrap();
|
||||
board::init();
|
||||
let (mut tec0, mut tec1) = init_pwm_tec();
|
||||
writeln!(stdout, "board initialized").unwrap();
|
||||
|
||||
println!(r#"
|
||||
|
@ -235,9 +265,11 @@ fn main() -> ! {
|
|||
let state = &mut states[usize::from(channel)];
|
||||
|
||||
if state.pid_enabled {
|
||||
let width = state.pid.update(data as f32) as u32;
|
||||
if channel == 0 { // TODO
|
||||
board::set_timer_pwm(width as u32, 0xffff);
|
||||
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),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -328,13 +360,15 @@ fn main() -> ! {
|
|||
}
|
||||
Command::Pwm { channel, mode: PwmMode::Manual { width, total }} => {
|
||||
states[channel].pid_enabled = false;
|
||||
board::set_timer_pwm(width, total);
|
||||
if channel == 0 { // TODO
|
||||
let _ = writeln!(
|
||||
socket, "channel {}: PWM duty cycle manually set to {}/{}",
|
||||
channel, width, total
|
||||
);
|
||||
match channel {
|
||||
0 => tec0.i_set.set(width, total),
|
||||
1 => tec1.i_set.set(width, total),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
let _ = writeln!(
|
||||
socket, "channel {}: PWM duty cycle manually set to {}/{}",
|
||||
channel, width, total
|
||||
);
|
||||
}
|
||||
Command::Pwm { channel, mode: PwmMode::Pid } => {
|
||||
states[channel].pid_enabled = true;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
use crate::board::pwm::PwmChannel;
|
||||
|
||||
/// 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,
|
||||
}
|
Loading…
Reference in New Issue