From a82ffadb35c609b4984f262faff7e907cc8cd329 Mon Sep 17 00:00:00 2001 From: Astro Date: Mon, 23 Sep 2019 16:50:43 +0200 Subject: [PATCH] add PWM abstraction for TEC control --- firmware/src/board/mod.rs | 57 +-------------- firmware/src/board/pwm.rs | 125 +++++++++++++++++++++++++++++++++ firmware/src/command_parser.rs | 6 +- firmware/src/main.rs | 54 +++++++++++--- firmware/src/tec.rs | 9 +++ 5 files changed, 182 insertions(+), 69 deletions(-) create mode 100644 firmware/src/board/pwm.rs create mode 100644 firmware/src/tec.rs diff --git a/firmware/src/board/mod.rs b/firmware/src/board/mod.rs index 303ae3c..a621443 100644 --- a/firmware/src/board/mod.rs +++ b/firmware/src/board/mod.rs @@ -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() }; diff --git a/firmware/src/board/pwm.rs b/firmware/src/board/pwm.rs new file mode 100644 index 0000000..003eebf --- /dev/null +++ b/firmware/src/board/pwm.rs @@ -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); diff --git a/firmware/src/command_parser.rs b/firmware/src/command_parser.rs index e33a8d7..6d67939 100644 --- a/firmware/src/command_parser.rs +++ b/firmware/src/command_parser.rs @@ -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> { +fn unsigned(input: &[u8]) -> IResult<&[u8], Result> { take_while1(is_digit)(input) .map(|(input, digits)| { let result = lexical::parse(digits) diff --git a/firmware/src/main.rs b/firmware/src/main.rs index fe8e172..73dbf2a 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -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, + TEC +) { + 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; diff --git a/firmware/src/tec.rs b/firmware/src/tec.rs new file mode 100644 index 0000000..ebb6dda --- /dev/null +++ b/firmware/src/tec.rs @@ -0,0 +1,9 @@ +use crate::board::pwm::PwmChannel; + +/// Thermo-Electric Cooling device controlled through PWM +pub struct TEC { + pub max_i_pos: MaxIPos, + pub max_i_neg: MaxINeg, + pub i_set: ISet, + pub max_v: MaxV, +}