#include <TimerOne.h> // github.com/PaulStoffregen/TimerOne
// This example creates a PWM signal with a variable duty cycle.
// This scheme enforces a minimum pulse width at low duty cycles
// by varying the frequency and increasing the off times between.
// for a 25kHz fan
// github.com/PaulStoffregen/TimerOne/blob/master/examples/FanSpeed/FanSpeed.ino
// Minimum pulse width could be important for good torque from motors
// With a tau=L/R, a 5*tau pulse width for a R=1.65ohm, L=3.2mH stepper
// would be tau=0.0032/1.65=1.9ms and 5*L/R = 5*.0032/1.65=9.7ms
constexpr int fanPin = 9; // Uno Timer1 OC1A pin
// Set the high frequency period and the minimum pulse
// These settings work for the Wokwi chips-scope from
//github.com/Dlloydev/Wokwi-Chip-Scope
// scope demo config: wokwi.com/projects/359331973918199809
// 2000us & 400us looks good with 200us sampling rate >22Hz
// 80 &4 at 1us sampling rate >1Hz
// 20000us and 2000us at 1ms sample rate for 50Hz>3Hz
// 20 & 1 and a real scope for 50K operation
constexpr unsigned long basePeriod = 20000;
constexpr unsigned long minUs = 2000;
// calculate the changeover from adjusting pulse width at const freq
// to adjusting the frequency at const pulse width:
constexpr float minDuty = 100.0 * minUs / basePeriod;
float dutyCycle = 0;
int lastA0 = 0;
void setup() { Serial.begin(9600); Timer1.initialize(basePeriod); } // us
void loop() { updateSettings(); }
void updateSettings() {
const int interval = 250;
static unsigned long last = -interval;
if (millis() - last < interval ) return;
last += interval;
int val = analogRead(A0);
if (val != lastA0) {
lastA0 = val;
dutyCycle = 100 * (1 - cos(PI / 2 * val / 1023)); // 0-100% with high res at low end
unsigned long myPeriod = 0;
Serial.print("PWM Fan, Duty Cycle = "); Serial.println(dutyCycle);
if (dutyCycle > minDuty || dutyCycle == 0.0) {
myPeriod = basePeriod;
Timer1.setPeriod(myPeriod);
Timer1.pwm(fanPin, (dutyCycle * 1023UL / 100));
Serial.print((int)(dutyCycle * myPeriod / 100));
Serial.print("us pulse at Hz:");
Serial.println(1000000UL / myPeriod);
} else { // Set by minimum width:
myPeriod = minUs * 100UL / dutyCycle;
if(myPeriod > 1885292UL) myPeriod = 1885292UL;
Timer1.setPeriod(myPeriod);
Timer1.pwm(fanPin, (uint32_t)(dutyCycle * 1023UL / 100));
Serial.print(minUs); Serial.print("us pulse / ");
Serial.print(myPeriod); Serial.print("us period / Hz:");
Serial.println(1.0e6 / myPeriod);
Serial.print("Timer1.OCR1A:"); Serial.print(OCR1A); // OCR1A - регистр
Serial.print(" / ICR1:"); Serial.print(ICR1); // ICR1 - регистр
Serial.print(" TCCR1B:0b"); Serial.println(TCCR1B,BIN); //TCCR1B - регистр
}
}
}
DutyCycle
100%
0%