Support fan PWM settings #73
|
@ -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`.
|
||||||
|
|||||||
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`,
|
||||||
|
|
|
@ -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),
|
||||||
sb10q
commented
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),
|
||||||
|
|
|
@ -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;
|
||||||
sb10q
commented
"weak" suggests amplitude, which is incorrect. "weak" suggests amplitude, which is incorrect.
-> "frequency is too low"
esavkin
commented
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.
sb10q
commented
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?
esavkin
commented
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
|
||||||
sb10q
commented
According to ? According to ?
esavkin
commented
Internal experiments Internal experiments
sb10q
commented
Please say so in the code comments. Please say so in the code comments.
sb10q
commented
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.
esavkin
commented
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);
|
||||||
sb10q
commented
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?
esavkin
commented
Fixed this and checked that there is no pulses by default. On oscilloscope there is pulse on 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,
|
||||||
sb10q
commented
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;
|
||||||
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 {
|
||||||
|
|
||||||
sb10q
commented
this doesn't need to be 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
sb10q
commented
Not sure if Not sure if ``[inline]`` here and in several other functions is relevant.
esavkin
commented
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
sb10q
commented
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();
|
||||||
sb10q
commented
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
10
src/main.rs
|
@ -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
sb10q
commented
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);
|
||||||
sb10q
commented
It's silly that What about just passing the few values required between 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?
esavkin
commented
indeed indeed
|
|||||||
if let Some(channel) = updated_channel {
|
if let Some(channel) = updated_channel {
|
||||||
sb10q
commented
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.
esavkin
commented
I do not have exact numbers on pulses missing IRL. The tested cases are:
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.
sb10q
commented
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.
esavkin
commented
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);
|
||||||
|
|
17
src/pins.rs
|
@ -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,
|
||||||
sb10q
commented
G99 is not a PWM fan. G99 is not a PWM fan.
|
|||||||
|
// so that it would produce less audible noise.
|
||||||
sb10q
commented
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.
sb10q
commented
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(),
|
||||||
sb10q
commented
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);
|
||||||
|
|
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.
"correlates with the square of the TEC's current" is incorrect