add PWM abstraction for TEC control

master
Astro 2019-09-23 16:50:43 +02:00
parent 8d01ca8d20
commit a82ffadb35
5 changed files with 182 additions and 69 deletions

View File

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

125
firmware/src/board/pwm.rs Normal file
View File

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

View File

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

View File

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

9
firmware/src/tec.rs Normal file
View File

@ -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,
}