Support fan PWM settings #73

Merged
sb10q merged 16 commits from esavkin/thermostat:69-fan_pwm into master 2023-03-22 17:15:49 +08:00
5 changed files with 46 additions and 60 deletions
Showing only changes of commit e6d928ef4e - Show all commits

View File

@ -279,7 +279,7 @@ The thermostat implements a PID control loop for each of the TEC channels, more
Fan control is available for the thermostat revisions with integrated fan system. For this purpose four commands are available: Fan control is available for the thermostat revisions with integrated fan system. For this purpose four commands are available:
1. `fan` - show fan stats: `fan_pwm`, `abs_max_tec_i`, `auto_mode`, `k_a`, `k_b`, `k_c`. 1. `fan` - show fan stats: `fan_pwm`, `abs_max_tec_i`, `auto_mode`, `k_a`, `k_b`, `k_c`.
2. `fan auto` - enable auto speed controller mode, which correlates with the square of the TEC's current. 2. `fan auto` - enable auto speed controller mode, which correlates with fan curve `fcurve`.
Outdated
Review

Doesn't the motor stall at low powers?

Doesn't the motor stall at low powers?

No, the minimum I tried was 0.008 PWM, anything below that makes the circuit consider there is no signal and it runs at full power. 0.008-0.04 values are working quite fine, though there is specific noise on low speed.

No, the minimum I tried was 0.008 PWM, anything below that makes the circuit consider there is no signal and it runs at full power. 0.008-0.04 values are working quite fine, though there is specific noise on low speed.
Outdated
Review

"correlates with the square of the TEC's current" is incorrect

"correlates with the square of the TEC's current" is incorrect
3. `fan <value>` - set the fan power with the value from `1` to `100` and disable auto mode. There is no way to disable the fan. 3. `fan <value>` - set the fan power with the value from `1` to `100` and disable auto mode. There is no way to disable the fan.
Please note that power doesn't correlate with the actual speed linearly. Please note that power doesn't correlate with the actual speed linearly.
4. `fcurve <a> <b> <c>` - set coefficients of the controlling curve `a*x^2 + b*x + c`, where `x` is `abs_max_tec_i/MAX_TEC_I`, 4. `fcurve <a> <b> <c>` - set coefficients of the controlling curve `a*x^2 + b*x + c`, where `x` is `abs_max_tec_i/MAX_TEC_I`,

View File

@ -381,29 +381,29 @@ impl Handler {
Ok(Handler::Handled) Ok(Handler::Handled)
} }
pub fn handle_command(command: Command, socket: &mut TcpSocket, session: &Session, leds: &mut Leds, store: &mut FlashStore, ipv4_config: &mut Ipv4Config, fan_ctrl: &mut FanCtrl) -> Result<Self, Error> { pub fn handle_command(command: Command, socket: &mut TcpSocket, channels: &mut Channels, session: &Session, leds: &mut Leds, store: &mut FlashStore, ipv4_config: &mut Ipv4Config, fan_ctrl: &mut FanCtrl) -> Result<Self, Error> {
match command { match command {
Command::Quit => Ok(Handler::CloseSocket), Command::Quit => Ok(Handler::CloseSocket),
Command::Reporting(_reporting) => Handler::reporting(socket), Command::Reporting(_reporting) => Handler::reporting(socket),
Outdated
Review

same

same
Command::Show(ShowCommand::Reporting) => Handler::show_report_mode(socket, session), Command::Show(ShowCommand::Reporting) => Handler::show_report_mode(socket, session),
Command::Show(ShowCommand::Input) => Handler::show_report(socket, &mut fan_ctrl.channels), Command::Show(ShowCommand::Input) => Handler::show_report(socket, channels),
Command::Show(ShowCommand::Pid) => Handler::show_pid(socket, &mut fan_ctrl.channels), Command::Show(ShowCommand::Pid) => Handler::show_pid(socket, channels),
Command::Show(ShowCommand::Pwm) => Handler::show_pwm(socket, &mut fan_ctrl.channels), Command::Show(ShowCommand::Pwm) => Handler::show_pwm(socket, channels),
Command::Show(ShowCommand::SteinhartHart) => Handler::show_steinhart_hart(socket, &mut fan_ctrl.channels), Command::Show(ShowCommand::SteinhartHart) => Handler::show_steinhart_hart(socket, channels),
Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, &mut fan_ctrl.channels), Command::Show(ShowCommand::PostFilter) => Handler::show_post_filter(socket, channels),
Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config), Command::Show(ShowCommand::Ipv4) => Handler::show_ipv4(socket, ipv4_config),
Command::PwmPid { channel } => Handler::engage_pid(socket, &mut fan_ctrl.channels, leds, channel), Command::PwmPid { channel } => Handler::engage_pid(socket, channels, leds, channel),
Command::Pwm { channel, pin, value } => Handler::set_pwm(socket, &mut fan_ctrl.channels, leds, channel, pin, value), Command::Pwm { channel, pin, value } => Handler::set_pwm(socket, channels, leds, channel, pin, value),
Command::CenterPoint { channel, center } => Handler::set_center_point(socket, &mut fan_ctrl.channels, channel, center), Command::CenterPoint { channel, center } => Handler::set_center_point(socket, channels, channel, center),
Command::Pid { channel, parameter, value } => Handler::set_pid(socket, &mut fan_ctrl.channels, channel, parameter, value), Command::Pid { channel, parameter, value } => Handler::set_pid(socket, channels, channel, parameter, value),
Command::SteinhartHart { channel, parameter, value } => Handler::set_steinhart_hart(socket, &mut fan_ctrl.channels, channel, parameter, value), Command::SteinhartHart { channel, parameter, value } => Handler::set_steinhart_hart(socket, channels, channel, parameter, value),
Command::PostFilter { channel, rate: None } => Handler::reset_post_filter(socket, &mut fan_ctrl.channels, channel), Command::PostFilter { channel, rate: None } => Handler::reset_post_filter(socket, channels, channel),
Command::PostFilter { channel, rate: Some(rate) } => Handler::set_post_filter(socket, &mut fan_ctrl.channels, channel, rate), Command::PostFilter { channel, rate: Some(rate) } => Handler::set_post_filter(socket, channels, channel, rate),
Command::Load { channel } => Handler::load_channel(socket, &mut fan_ctrl.channels, store, channel), Command::Load { channel } => Handler::load_channel(socket, channels, store, channel),
Command::Save { channel } => Handler::save_channel(socket, &mut fan_ctrl.channels, channel, store), Command::Save { channel } => Handler::save_channel(socket, channels, channel, store),
Command::Ipv4(config) => Handler::set_ipv4(socket, store, config), Command::Ipv4(config) => Handler::set_ipv4(socket, store, config),
Command::Reset => Handler::reset(&mut fan_ctrl.channels), Command::Reset => Handler::reset(channels),
Command::Dfu => Handler::dfu(&mut fan_ctrl.channels), Command::Dfu => Handler::dfu(channels),
Command::FanSet {fan_pwm} => Handler::set_fan(socket, fan_pwm, fan_ctrl), Command::FanSet {fan_pwm} => Handler::set_fan(socket, fan_pwm, fan_ctrl),
Command::ShowFan => Handler::show_fan(socket, fan_ctrl), Command::ShowFan => Handler::show_fan(socket, fan_ctrl),
Command::FanAuto => Handler::fan_auto(socket, fan_ctrl), Command::FanAuto => Handler::fan_auto(socket, fan_ctrl),

View File

@ -1,3 +1,4 @@
use num_traits::Float;
use serde::Serialize; use serde::Serialize;
use stm32f4xx_hal::{ use stm32f4xx_hal::{
pwm::{self, PwmChannels}, pwm::{self, PwmChannels},
@ -6,7 +7,7 @@ use stm32f4xx_hal::{
use crate::{ use crate::{
pins::HWRevPins, pins::HWRevPins,
channels::{Channels, JsonBuffer}, channels::JsonBuffer,
}; };
pub type FanPin = PwmChannels<TIM8, pwm::C4>; pub type FanPin = PwmChannels<TIM8, pwm::C4>;
@ -17,8 +18,8 @@ const MAX_TEC_I: f64 = 3.0;
const MAX_USER_FAN_PWM: f64 = 100.0; const MAX_USER_FAN_PWM: f64 = 100.0;
const MIN_USER_FAN_PWM: f64 = 1.0; const MIN_USER_FAN_PWM: f64 = 1.0;
const MAX_FAN_PWM: f64 = 1.0; const MAX_FAN_PWM: f64 = 1.0;
Outdated
Review

"weak" suggests amplitude, which is incorrect.
-> "frequency is too low"

"weak" suggests amplitude, which is incorrect. -> "frequency is too low"

no, not the frequency. The near-zero freq is the consequence of weak signal.

no, not the frequency. The near-zero freq is the consequence of weak signal.
Outdated
Review

Do you have an oscilloscope picture demonstrating this, and how was it measured?

Do you have an oscilloscope picture demonstrating this, and how was it measured?

We used pulse counter to measure the tacho values, for PWM 0.04-1.0 it was ok, but for lower the tacho signal disappears

We used pulse counter to measure the tacho values, for PWM 0.04-1.0 it was ok, but for lower the tacho signal disappears
// below this value, motor pulse signal is too weak // below this value motor circuit assumes there is no signal and runs full speed
Outdated
Review

According to ?

According to ?

Internal experiments

Internal experiments
Outdated
Review

Please say so in the code comments.

Please say so in the code comments.
Outdated
Review

Also we can probably expect this to be hardware revision dependent. When the fan has proper PWM support this shouldn't be an issue I guess.

Also we can probably expect this to be hardware revision dependent. When the fan has proper PWM support this shouldn't be an issue I guess.

Made this (and others) value coming from hwrev

Made this (and others) value coming from hwrev
const MIN_FAN_PWM: f64 = 0.05; const MIN_FAN_PWM: f64 = 0.02;
const DEFAULT_K_A: f64 = 1.0; const DEFAULT_K_A: f64 = 1.0;
const DEFAULT_K_B: f64 = 0.0; const DEFAULT_K_B: f64 = 0.0;
@ -38,12 +39,12 @@ pub struct FanCtrl {
k_a: f64, k_a: f64,
k_b: f64, k_b: f64,
k_c: f64, k_c: f64,
pub channels: Channels, abs_max_tec_i: f64,
} }
impl FanCtrl { impl FanCtrl {
pub fn new(mut fan: FanPin, channels: Channels) -> Self { pub fn new(mut fan: FanPin, hwrev: &HWRev) -> Self {
let available = channels.hwrev.fan_available(); let available = hwrev.fan_available();
if available { if available {
fan.set_duty(0); fan.set_duty(0);
Outdated
Review

Did you double-check that this results in no PWM pulses being emitted at all, even short ones?

Did you double-check that this results in no PWM pulses being emitted at all, even short ones?

Fixed this and checked that there is no pulses by default. On oscilloscope there is pulse on fan 100 command, but no such pulse on reset command

Fixed this and checked that there is no pulses by default. On oscilloscope there is pulse on `fan 100` command, but no such pulse on `reset` command
@ -57,11 +58,12 @@ impl FanCtrl {
k_a: DEFAULT_K_A, k_a: DEFAULT_K_A,
Outdated
Review

at user's own risk

at user's own risk
k_b: DEFAULT_K_B, k_b: DEFAULT_K_B,
k_c: DEFAULT_K_C, k_c: DEFAULT_K_C,
channels, abs_max_tec_i: 0f64,
} }
} }
pub fn cycle(&mut self) { pub fn cycle(&mut self, abs_max_tec_i: f64) {
self.abs_max_tec_i = abs_max_tec_i;
Review

inline

inline
self.adjust_speed(); self.adjust_speed();
} }
@ -69,7 +71,7 @@ impl FanCtrl {
if self.available { if self.available {
let summary = FanSummary { let summary = FanSummary {
fan_pwm: self.get_pwm(), fan_pwm: self.get_pwm(),
abs_max_tec_i: self.channels.current_abs_max_tec_i(), abs_max_tec_i: self.abs_max_tec_i,
auto_mode: self.fan_auto, auto_mode: self.fan_auto,
k_a: self.k_a, k_a: self.k_a,
k_b: self.k_b, k_b: self.k_b,
@ -84,26 +86,23 @@ impl FanCtrl {
Review

this doesn't need to be pub

this doesn't need to be ``pub``
pub fn adjust_speed(&mut self) { pub fn adjust_speed(&mut self) {
if self.fan_auto && self.available { if self.fan_auto && self.available {
let scaled_current = self.channels.current_abs_max_tec_i() / MAX_TEC_I; let scaled_current = self.abs_max_tec_i / MAX_TEC_I;
// do not limit upper bound, as it will be limited in the set_pwm() // do not limit upper bound, as it will be limited in the set_pwm()
let pwm = (MAX_USER_FAN_PWM * (scaled_current * (scaled_current * self.k_a + self.k_b) + self.k_c)) as u32; let pwm = (MAX_USER_FAN_PWM * (scaled_current * (scaled_current * self.k_a + self.k_b) + self.k_c)) as u32;
self.set_pwm(pwm); self.set_pwm(pwm);
} }
} }
#[inline]
pub fn set_auto_mode(&mut self, fan_auto: bool) { pub fn set_auto_mode(&mut self, fan_auto: bool) {
self.fan_auto = fan_auto; self.fan_auto = fan_auto;
} }
Outdated
Review

Not sure if [inline] here and in several other functions is relevant.

Not sure if ``[inline]`` here and in several other functions is relevant.

These functions are short, used just in a very limited number of places. They all look to be a good candidate to be inlined

These functions are short, used just in a very limited number of places. They all look to be a good candidate to be inlined
Outdated
Review

Probably the compiler can figure this out already, and it doesn't seem to be performance-critical code so it doesn't matter if it doesn't.

Probably the compiler can figure this out already, and it doesn't seem to be performance-critical code so it doesn't matter if it doesn't.
#[inline]
pub fn set_curve(&mut self, k_a: f64, k_b: f64, k_c: f64) { pub fn set_curve(&mut self, k_a: f64, k_b: f64, k_c: f64) {
self.k_a = k_a; self.k_a = k_a;
self.k_b = k_b; self.k_b = k_b;
self.k_c = k_c; self.k_c = k_c;
} }
#[inline]
pub fn restore_defaults(&mut self) { pub fn restore_defaults(&mut self) {
self.set_curve(DEFAULT_K_A, DEFAULT_K_B, DEFAULT_K_C); self.set_curve(DEFAULT_K_A, DEFAULT_K_B, DEFAULT_K_C);
} }
@ -117,7 +116,6 @@ impl FanCtrl {
value as f64 / (max as f64) value as f64 / (max as f64)
} }
#[inline]
fn scale_number(unscaled: f64, to_min: f64, to_max: f64, from_min: f64, from_max: f64) -> f64 { fn scale_number(unscaled: f64, to_min: f64, to_max: f64, from_min: f64, from_max: f64) -> f64 {
(to_max - to_min) * (unscaled - from_min) / (from_max - from_min) + to_min (to_max - to_min) * (unscaled - from_min) / (from_max - from_min) + to_min
} }
@ -125,7 +123,7 @@ impl FanCtrl {
fn get_pwm(&self) -> u32 { fn get_pwm(&self) -> u32 {
let duty = self.fan.get_duty(); let duty = self.fan.get_duty();
let max = self.fan.get_max_duty(); let max = self.fan.get_max_duty();
Outdated
Review

this does not need to be a class member

this does not need to be a class member
(Self::scale_number(duty as f64 / (max as f64), MIN_USER_FAN_PWM, MAX_USER_FAN_PWM, MIN_FAN_PWM, MAX_FAN_PWM) + 0.5) as u32 Self::scale_number(duty as f64 / (max as f64), MIN_USER_FAN_PWM, MAX_USER_FAN_PWM, MIN_FAN_PWM, MAX_FAN_PWM).round() as u32
} }
} }
Outdated
Review

.round() not +0.5

.round() not +0.5
@ -155,18 +153,3 @@ pub struct FanSummary {
k_b: f64, k_b: f64,
k_c: f64, k_c: f64,
} }
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_scaler() {
for x in 1..100 {
assert_eq!((FanCtrl::scale_number(
FanCtrl::scale_number(x as f64, MIN_FAN_PWM, MAX_FAN_PWM, MIN_USER_FAN_PWM, MAX_USER_FAN_PWM),
MIN_USER_FAN_PWM, MAX_USER_FAN_PWM, MIN_FAN_PWM, MAX_FAN_PWM) + 0.5) as i32,
x);
}
}
}

View File

@ -150,7 +150,7 @@ fn main() -> ! {
} }
} }
let mut fan_ctrl = FanCtrl::new(fan, channels); let mut fan_ctrl = FanCtrl::new(fan, &channels.hwrev);
// default net config: // default net config:
let mut ipv4_config = Ipv4Config { let mut ipv4_config = Ipv4Config {
esavkin marked this conversation as resolved Outdated
Outdated
Review

Highly suspicious. I doubt either mutex or unsafe is necessary.

Highly suspicious. I doubt either mutex or unsafe is necessary.
@ -180,12 +180,12 @@ fn main() -> ! {
loop { loop {
let mut new_ipv4_config = None; let mut new_ipv4_config = None;
let instant = Instant::from_millis(i64::from(timer::now())); let instant = Instant::from_millis(i64::from(timer::now()));
let updated_channel = fan_ctrl.channels.poll_adc(instant); let updated_channel = channels.poll_adc(instant);
Outdated
Review

It's silly that fan_ctrl owns channels, don't you think?

What about just passing the few values required between channels and fan_ctrl in main, instead of this broken abstraction?

It's silly that ``fan_ctrl`` owns ``channels``, don't you think? What about just passing the few values required between ``channels`` and ``fan_ctrl`` in ``main``, instead of this broken abstraction?

indeed

indeed
if let Some(channel) = updated_channel { if let Some(channel) = updated_channel {
Outdated
Review

I don't see why you keep talking about precision. Is the current code missing pulses? If not, precision should be more than sufficient already.

I don't see why you keep talking about precision. Is the current code missing pulses? If not, precision should be more than sufficient already.

I do not have exact numbers on pulses missing IRL. The tested cases are:

  1. Increasing PWM should lead to increased tacho number.
  2. Manually lowering the fan speed should lead to lower numbers.
I do not have exact numbers on pulses missing IRL. The tested cases are: 1. Increasing PWM should lead to increased tacho number. 2. Manually lowering the fan speed should lead to lower numbers.
Outdated
Review

Just use the oscilloscope on the tacho signal and check if the frequency reported on the oscilloscope matches what you get from your firmware at various fan speeds and particularly at maximum fan speed with concurrent network traffic.

Just use the oscilloscope on the tacho signal and check if the frequency reported on the oscilloscope matches what you get from your firmware at various fan speeds and particularly at maximum fan speed with concurrent network traffic.

Oscilloscope shows similar values

Oscilloscope shows similar values
server.for_each(|_, session| session.set_report_pending(channel.into())); server.for_each(|_, session| session.set_report_pending(channel.into()));
} }
fan_ctrl.cycle(); fan_ctrl.cycle(channels.current_abs_max_tec_i());
let instant = Instant::from_millis(i64::from(timer::now())); let instant = Instant::from_millis(i64::from(timer::now()));
cortex_m::interrupt::free(net::clear_pending); cortex_m::interrupt::free(net::clear_pending);
@ -210,7 +210,7 @@ fn main() -> ! {
// Do nothing and feed more data to the line reader in the next loop cycle. // Do nothing and feed more data to the line reader in the next loop cycle.
Ok(SessionInput::Nothing) => {} Ok(SessionInput::Nothing) => {}
Ok(SessionInput::Command(command)) => { Ok(SessionInput::Command(command)) => {
match Handler::handle_command(command, &mut socket, session, &mut leds, &mut store, &mut ipv4_config, &mut fan_ctrl) { match Handler::handle_command(command, &mut socket, &mut channels, session, &mut leds, &mut store, &mut ipv4_config, &mut fan_ctrl) {
Ok(Handler::NewIPV4(ip)) => new_ipv4_config = Some(ip), Ok(Handler::NewIPV4(ip)) => new_ipv4_config = Some(ip),
Ok(Handler::Handled) => {}, Ok(Handler::Handled) => {},
Ok(Handler::CloseSocket) => socket.close(), Ok(Handler::CloseSocket) => socket.close(),
@ -227,7 +227,7 @@ fn main() -> ! {
} }
} else if socket.can_send() { } else if socket.can_send() {
if let Some(channel) = session.is_report_pending() { if let Some(channel) = session.is_report_pending() {
match fan_ctrl.channels.reports_json() { match channels.reports_json() {
Ok(buf) => { Ok(buf) => {
send_line(&mut socket, &buf[..]); send_line(&mut socket, &buf[..]);
session.mark_report_sent(channel); session.mark_report_sent(channel);

View File

@ -26,7 +26,7 @@ use stm32f4xx_hal::{
TIM1, TIM3, TIM8 TIM1, TIM3, TIM8
}, },
timer::Timer, timer::Timer,
time::{U32Ext, KiloHertz}, time::U32Ext,
}; };
use eeprom24x::{self, Eeprom24x}; use eeprom24x::{self, Eeprom24x};
use stm32_eth::EthPins; use stm32_eth::EthPins;
@ -36,8 +36,6 @@ use crate::{
fan_ctrl::FanPin fan_ctrl::FanPin
}; };
const PWM_FREQ: KiloHertz = KiloHertz(20u32);
pub type Eeprom = Eeprom24x< pub type Eeprom = Eeprom24x<
I2c<I2C1, ( I2c<I2C1, (
PB8<AlternateOD<{ stm32f4xx_hal::gpio::AF4 }>>, PB8<AlternateOD<{ stm32f4xx_hal::gpio::AF4 }>>,
@ -228,7 +226,11 @@ impl Pins {
hclk: clocks.hclk(), hclk: clocks.hclk(),
}; };
let fan = Timer::new(tim8, &clocks).pwm(gpioc.pc9.into_alternate(), PWM_FREQ); // Though there is not enough evidence available for concrete fan model,
// it is generally advised to have higher PWM frequencies for brushless motors,
Outdated
Review

G99 is not a PWM fan.

G99 is not a PWM fan.
// so that it would produce less audible noise.
Outdated
Review

Advised where? All the SUNON docs say PWM on this fan model isn't supported at all.

Advised where? All the SUNON docs say PWM on this fan model isn't supported at all.
Outdated
Review

And what "higher frequency" are you talking about?

And what "higher frequency" are you talking about?
// Source: https://www.controleng.com/articles/understanding-the-effect-of-pwm-when-controlling-a-brushless-dc-motor/
let fan = Timer::new(tim8, &clocks).pwm(gpioc.pc9.into_alternate(), 80u32.khz());
(pins, leds, eeprom, eth_pins, usb, fan) (pins, leds, eeprom, eth_pins, usb, fan)
} }
@ -312,6 +314,7 @@ impl PwmPins {
max_i_neg0: PE13<M5>, max_i_neg0: PE13<M5>,
max_i_neg1: PE14<M6>, max_i_neg1: PE14<M6>,
) -> PwmPins { ) -> PwmPins {
let freq = 20u32.khz();
fn init_pwm_pin<P: hal::PwmPin<Duty=u16>>(pin: &mut P) { fn init_pwm_pin<P: hal::PwmPin<Duty=u16>>(pin: &mut P) {
pin.set_duty(0); pin.set_duty(0);
@ -321,8 +324,8 @@ impl PwmPins {
max_v0.into_alternate(), max_v0.into_alternate(),
max_v1.into_alternate(), max_v1.into_alternate(),
Outdated
Review

No.

No.
); );
//let (mut max_v0, mut max_v1) = pwm::tim3(tim3, channels, clocks, PWM_FREQ); //let (mut max_v0, mut max_v1) = pwm::tim3(tim3, channels, clocks, freq);
let (mut max_v0, mut max_v1) = Timer::new(tim3, &clocks).pwm(channels, PWM_FREQ); let (mut max_v0, mut max_v1) = Timer::new(tim3, &clocks).pwm(channels, freq);
init_pwm_pin(&mut max_v0); init_pwm_pin(&mut max_v0);
init_pwm_pin(&mut max_v1); init_pwm_pin(&mut max_v1);
@ -333,7 +336,7 @@ impl PwmPins {
max_i_neg1.into_alternate(), max_i_neg1.into_alternate(),
); );
let (mut max_i_pos0, mut max_i_pos1, mut max_i_neg0, mut max_i_neg1) = let (mut max_i_pos0, mut max_i_pos1, mut max_i_neg0, mut max_i_neg1) =
Timer::new(tim1, &clocks).pwm(channels, PWM_FREQ); Timer::new(tim1, &clocks).pwm(channels, freq);
init_pwm_pin(&mut max_i_pos0); init_pwm_pin(&mut max_i_pos0);
init_pwm_pin(&mut max_i_neg0); init_pwm_pin(&mut max_i_neg0);
init_pwm_pin(&mut max_i_pos1); init_pwm_pin(&mut max_i_pos1);