Dear all,
in my current project I am using a Noctua NF-A14 DC 5V DC fan.
Thanks to the help on timer2 I managed to drive the fan on the required 25kHz frequency. In the code below, by setting the "var" variable to a value between 0 and 40 it is possible to set different fan speeds.
Now, I would like to calculate the RPM by using the input capture pin and timer1. Using some tutorials and this example I combined the following code:
/* 12.06.2021
* (Mini pro 168 5V 16MHz)
* Using PWM @ 25 KHz to drive a Noctua 5V PWM fan
* For Control want to use 16 MHz on Timer2 (without library)
* For measuring the RPM Signal of the fan use input capture pin with timer1
*
*/
#define PWM_FAN_PIN 3 // blue cable
#define RPM_FAN_PIN 8 //green cable = Pin 8 ist der Input Capture Pin
//yellow is 5V
//Serial Communication speed
#define BAUDRATE 115200
const unsigned int top = 40; //defines the Frequency 24,4 kHz
const unsigned int timer1End = 62499;
volatile bool RPM_MEASURE = false;
volatile unsigned int timer1_capture_counter = 0;
unsigned int RPMspeed = 0;
byte var = 0;
void setupTimer2();
void setupTimer1();
void setFanSpeed(unsigned int val);
void calcFanSpeed();
//gets triggered every 1s - see setup of timer1 in CTC! mode
ISR(TIMER1_COMPA_vect){
RPM_MEASURE = true;
}
ISR(TIMER1_CAPT_vect){
timer1_capture_counter ++;
}
void setupTimer1(){
/*Set detection to 1 Hz
* f = 16MHz / (prescaler x (65536 + TopVal))
* With prescaler 256 -> TopVal = 62499 for 1Hz
*/
//Clear Timer1 control and Count registers
TCCR1A = 0; // undo configurations on Timer1 pin 9
TCCR1B = 0; // undo configurations on Timer1 pin 10
TCNT1 = 0; // Reset Timer1
TCCR1A = 0; //CTC mode = 4 -> WGM(13,12,11,10) = (0,1,0,0)
TCCR1B = (1<<ICES1) +(1<<WGM12)+ (1<<CS12); // CS12 = 1 -> prescaler 256; ICES1 = 1 means rising edge; ICNC1 = noise canceler
TIMSK1 = (1<<ICIE1) + (1<<OCIE1A); // ICIE1 -> activates input capture interrupt, OCIE1A -> Output Compare A Match Flag
OCR1A = timer1End;
return;
}
void setupTimer2(){
//Clear Timer2 control and Count registers
TCCR2A = 0; // undo configurations on Timer2 pin 11
TCCR2B = 0; // undo configurations on Timer2 pin 3
TCNT2 = 0; // Reset Register of Timer2
//Setting wave form generation mode (WGM) to PWM, phase correct mode 5
// set prescaler to 8 -> CS12=0; CS11=1; CS10=0;
TCCR2A = (1<<COM2B1) | (1<<WGM20);
TCCR2B = (1<<WGM22) | (1<<CS21);
OCR2A = top;
return;
}
void setFanSpeed( unsigned int val){
if(val <= top)
OCR2B = val;
else{
Serial.println(F("Invalid fan speed"));
OCR2B = 0;
}
return;
}
void calcFanSpeed(){
RPM_MEASURE = false;
RPMspeed = (unsigned int)timer1_capture_counter / 2;
timer1_capture_counter = 0;
return;
}
void setup() {
while(!Serial);
Serial.begin(BAUDRATE);
Serial.println(F("Start Operation - setup"));
setupTimer2();
setupTimer1();
pinMode(PWM_FAN_PIN, OUTPUT);
pinMode(RPM_FAN_PIN, INPUT);
}
void loop() {
var = 20;
setFanSpeed(var);
if(RPM_MEASURE == true){
calcFanSpeed();
Serial.print(F("PWM Speed: "));
Serial.print(var);
Serial.print(F(" corres. to RPM: "));
Serial.println(RPMspeed);
}
}
The program compiles and works great on all var values, but the measured RPM is wrong.
If I set a low speed of var = 10 the µC captures in "timer1_capture_counter" a higher value than at var = 20 (where the fan runs obviously faster).
Ridiculously, at a quite high rotation value of var = 35, I get an output of timer1_capture_counter = 0 counts (within one second).
According to the Noctua description I should get 25kHz x 2 = 50.000 counts. (Two rising flags per cycle).
The problem is independent of the used mode (4 = CTC above). I measured same wrong values in normal mode ( 0, with correspondingly addapted settings of the timer1).
Where is my mistake? Or is it on the electronics side, that the output waveform is unreadable for the arduino? Unfortunately, I do not have an oscilloscope to check the generated waveform...
Thank you very much in advance!
PS: I avoided to use an ext. Interrupt to count the pulses, because teh gain was to run it in the background without performance losses.