Arduino Nano 25kHz 3 Fans PWM and Tachomer

Hi,
Could some one help to understand tachometer issue.

I complied a debug sketch to see tachometers data, and Can’t get real rmp of 3 fans PWM 4pin.

All fans connected:
1pin - Ground to PS
2pin - 12V to PS
3pin - Tach with PullUp resitor 5,1kOm to 5V Nano
4pin - PWM to Nano

So, I tried more than 10 different options to get real rmp data of coolers that I Can found. I tried digital pins (5, 6, 7) in different Ports and many other.
But I reached a dead end. I understand that such RPM readings are presumably related to an increase in frequency 25kHz, but I can’t figure it out further.
The PWM of 3 fans is excellent, a rise every 10% is clearly audible. However, at 100% the speed starts to float.

Below is the rpm data, first column is PWM in %

Pulse counter started on A1(A)/A2(B)/A3(C)
0   | fan1: 81 | fan2: 91 | fan3: 83
0   | fan1: 54 | fan2: 55 | fan3: 50
0   | fan1: 26 | fan2: 23 | fan3: 22
0   | fan1: 15 | fan2: 14 | fan3: 14
0   | fan1: 14 | fan2: 14 | fan3: 14
0   | fan1: 13 | fan2: 14 | fan3: 14
0   | fan1: 14 | fan2: 14 | fan3: 13
0   | fan1: 14 | fan2: 13 | fan3: 14
0   | fan1: 14 | fan2: 15 | fan3: 15

10   | fan1: 15 | fan2: 15 | fan3: 15
10   | fan1: 2738 | fan2: 2260 | fan3: 2753
10   | fan1: 4439 | fan2: 3733 | fan3: 4465
10   | fan1: 1136 | fan2: 940 | fan3: 1158
10   | fan1: 669 | fan2: 540 | fan3: 686
10   | fan1: 1161 | fan2: 957 | fan3: 1178
10   | fan1: 219 | fan2: 198 | fan3: 220
10   | fan1: 548 | fan2: 461 | fan3: 568
10   | fan1: 2552 | fan2: 2125 | fan3: 2649
10   | fan1: 2058 | fan2: 1720 | fan3: 2153

20   | fan1: 1946 | fan2: 1639 | fan3: 2027
20   | fan1: 1497 | fan2: 1396 | fan3: 1506
20   | fan1: 2285 | fan2: 2158 | fan3: 2307
20   | fan1: 3260 | fan2: 3071 | fan3: 3276
20   | fan1: 3554 | fan2: 3357 | fan3: 3561
20   | fan1: 4041 | fan2: 3830 | fan3: 4031
20   | fan1: 4166 | fan2: 3899 | fan3: 4185
20   | fan1: 4610 | fan2: 4315 | fan3: 4577
20   | fan1: 4506 | fan2: 4236 | fan3: 4483
20   | fan1: 3727 | fan2: 3494 | fan3: 3728
20   | fan1: 3625 | fan2: 3435 | fan3: 3653

30   | fan1: 4946 | fan2: 4876 | fan3: 4936
30   | fan1: 5012 | fan2: 4971 | fan3: 5016
30   | fan1: 4373 | fan2: 4304 | fan3: 4360
30   | fan1: 3615 | fan2: 3578 | fan3: 3624
30   | fan1: 3741 | fan2: 3664 | fan3: 3710
30   | fan1: 5087 | fan2: 5010 | fan3: 5084
30   | fan1: 3965 | fan2: 3893 | fan3: 3940
30   | fan1: 2691 | fan2: 2643 | fan3: 2694
30   | fan1: 4813 | fan2: 4739 | fan3: 4788
30   | fan1: 4653 | fan2: 4594 | fan3: 4661

40   | fan1: 11047 | fan2: 3605 | fan3: 3604
40   | fan1: 10975 | fan2: 4000 | fan3: 4000
40   | fan1: 9822 | fan2: 4378 | fan3: 4377
40   | fan1: 11245 | fan2: 3571 | fan3: 3571
40   | fan1: 11094 | fan2: 4365 | fan3: 4363
40   | fan1: 10525 | fan2: 4081 | fan3: 4093
40   | fan1: 10226 | fan2: 4104 | fan3: 4094
40   | fan1: 11082 | fan2: 4268 | fan3: 4285
40   | fan1: 10353 | fan2: 3764 | fan3: 3756
40   | fan1: 11194 | fan2: 4331 | fan3: 4328

50   | fan1: 11071 | fan2: 3878 | fan3: 3891
50   | fan1: 11573 | fan2: 4220 | fan3: 4203
50   | fan1: 10240 | fan2: 5047 | fan3: 5043
50   | fan1: 10955 | fan2: 4093 | fan3: 4095
50   | fan1: 11432 | fan2: 3886 | fan3: 3875
50   | fan1: 11215 | fan2: 4769 | fan3: 4757
50   | fan1: 10778 | fan2: 4317 | fan3: 4318
50   | fan1: 11070 | fan2: 4569 | fan3: 4567
50   | fan1: 11177 | fan2: 4325 | fan3: 4310
50   | fan1: 11204 | fan2: 4110 | fan3: 4125

60   | fan1: 8351 | fan2: 11587 | fan3: 10952
60   | fan1: 8367 | fan2: 10749 | fan3: 11358
60   | fan1: 7892 | fan2: 10636 | fan3: 11264
60   | fan1: 7729 | fan2: 10807 | fan3: 10744
60   | fan1: 7784 | fan2: 11085 | fan3: 11318
60   | fan1: 8397 | fan2: 11097 | fan3: 11439
60   | fan1: 8283 | fan2: 11143 | fan3: 11698
60   | fan1: 8759 | fan2: 10646 | fan3: 11089
60   | fan1: 8039 | fan2: 10955 | fan3: 10721
60   | fan1: 7169 | fan2: 10855 | fan3: 11174

70   | fan1: 8419 | fan2: 9882 | fan3: 10153
70   | fan1: 8413 | fan2: 10011 | fan3: 11372
70   | fan1: 8349 | fan2: 9717 | fan3: 10555
70   | fan1: 9456 | fan2: 10336 | fan3: 10732
70   | fan1: 8013 | fan2: 9416 | fan3: 10709
70   | fan1: 8651 | fan2: 10115 | fan3: 11081
70   | fan1: 8108 | fan2: 10409 | fan3: 10475
70   | fan1: 8583 | fan2: 9428 | fan3: 10745
70   | fan1: 9088 | fan2: 10158 | fan3: 10841
70   | fan1: 8025 | fan2: 10094 | fan3: 10484

80   | fan1: 4656 | fan2: 8076 | fan3: 9300
80   | fan1: 5084 | fan2: 8798 | fan3: 9327
80   | fan1: 4474 | fan2: 7345 | fan3: 8516
80   | fan1: 4897 | fan2: 8613 | fan3: 9499
80   | fan1: 4903 | fan2: 8196 | fan3: 8989
80   | fan1: 4698 | fan2: 8530 | fan3: 9434
80   | fan1: 4556 | fan2: 7607 | fan3: 8713
80   | fan1: 5007 | fan2: 8629 | fan3: 9385
80   | fan1: 4920 | fan2: 8121 | fan3: 9018
80   | fan1: 4505 | fan2: 8237 | fan3: 9392

90   | fan1: 3969 | fan2: 4771 | fan3: 6373
90   | fan1: 3333 | fan2: 5422 | fan3: 7817
90   | fan1: 3387 | fan2: 5032 | fan3: 7058
90   | fan1: 3659 | fan2: 5486 | fan3: 6288
90   | fan1: 3280 | fan2: 5418 | fan3: 7209
90   | fan1: 3850 | fan2: 5053 | fan3: 6347
90   | fan1: 3875 | fan2: 5254 | fan3: 6600
90   | fan1: 2938 | fan2: 5745 | fan3: 7366
90   | fan1: 3793 | fan2: 5253 | fan3: 6597
90   | fan1: 3751 | fan2: 5106 | fan3: 6311

100   | fan1: 0 | fan2: 0 | fan3: 2
100   | fan1: 0 | fan2: 0 | fan3: 0
100   | fan1: 0 | fan2: 0 | fan3: 0
100   | fan1: 0 | fan2: 0 | fan3: 0
100   | fan1: 0 | fan2: 0 | fan3: 0
100   | fan1: 0 | fan2: 0 | fan3: 0
100   | fan1: 0 | fan2: 0 | fan3: 0
100   | fan1: 0 | fan2: 0 | fan3: 0
100   | fan1: 1 | fan2: 0 | fan3: 0
100   | fan1: 0 | fan2: 0 | fan3: 0

Below a debug sketch:
(I apologize for the dirt in the sketch, I just chose the Tachometer functionality for debugging.)

const int fanPwmPins[] = {3, 9, 10};  // PWM Pins (D3, D9, D10) (25kHz)
unsigned long Millis_current;
unsigned long Millis_fan;
unsigned long Millis_cooler;
const unsigned long period_cooler = 10000;
const unsigned long period_fan = 1000;
const int power_ps24v = 7 ;
const int power_cooler = 11;
const int power_rpi4 = 12;
const int button_sw = 2;
const int button_led = 8;
int rpm_value = 100;
int rpm_change = 10;

unsigned long a1;
unsigned long a2;
unsigned long a3;

// Pulse counter on A1, A2, A3 for Arduino Nano (ATmega328P)
// A1 -> PC1 (PCINT9), A2 -> PC2 (PCINT10), A3 -> PC3 (PCINT11)

volatile unsigned long countA1 = 0;
volatile unsigned long countA2 = 0;
volatile unsigned long countA3 = 0;

// store previous states to detect edges
volatile uint8_t prevPortC;

void setPWM(int fanIdx, int duty) {
duty = constrain(duty, 0, 100);
if (fanIdx == 0) { // Pin 3
OCR2B = (duty * 79) / 100;
} else if (fanIdx == 1) { // Pin 9
OCR1A = (duty * 320) / 100;
} else if (fanIdx == 2) { // Pin 10
OCR1B = (duty * 320) / 100;
}
}

void setup() {

pinMode(button_sw, INPUT);
pinMode(button_led, OUTPUT);
pinMode(power_ps24v, OUTPUT);
pinMode(power_cooler, OUTPUT);
pinMode(power_rpi4, OUTPUT);
digitalWrite(power_ps24v, HIGH);
digitalWrite(power_cooler, HIGH);
digitalWrite(power_rpi4, HIGH);

// Configure PWM Timers for 25kHz PWM
// Timer 1 (Pins 9, 10) and Timer 2 (Pin 3)

TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM11); // Phase Correct PWM
TCCR1B = _BV(WGM13) | _BV(CS10);                 // Prescaler 1
ICR1 = 320;                                      // 16MHz / (2 * 1 * 25000) = 320

TCCR2A = _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);  // Fast PWM Pin 3
TCCR2B = _BV(WGM22) | _BV(CS21);                 // Prescaler 8
OCR2A = 79;                                      // 16MHz / (8 * 25000) - 1 = 79

// Initialize Pins PWN and Tach
for (int i = 0; i < 3; i++) {
pinMode(fanPwmPins[i], OUTPUT);
}

// Start fans at 100% at starting
for (int i = 0; i < 3; i++) { setPWM(i, 100); }

// Configure pins as inputs (analog pins default to input)
pinMode(A1, INPUT);
pinMode(A2, INPUT);
pinMode(A3, INPUT);

// Optional pullups - enable if you need defined low/high when open
// digitalWrite(A1, HIGH);
// digitalWrite(A2, HIGH);
// digitalWrite(A3, HIGH);

// Read initial PORTC state
prevPortC = PINC;

// Enable pin change interrupts for PCINT9,10,11 (PC1,PC2,PC3)
// PCMSK1 corresponds to PCINT[14:8]; set bits 9-11 -> mask bits 1-3
PCMSK1 |= (1 << PCINT9) | (1 << PCINT10) | (1 << PCINT11); // same as (1<<PC1)|(1<<PC2)|(1<<PC3)

// Enable PCINT1 group
PCICR |= (1 << PCIE1);

Serial.begin(9600);
while (!Serial) ; // wait for Serial on some boards (safe)
Serial.println("Pulse counter started on A1(A)/A2(B)/A3(C)");

digitalWrite(power_cooler, LOW);

Millis_fan = millis();
}

ISR(PCINT1_vect) {
uint8_t curr = PINC;
uint8_t changed = curr ^ prevPortC;

// Check PC1 (A1) rising edge: bit 1 changed and now high
if (changed & (1 << PC1)) {
if (curr & (1 << PC1)) {
countA1++;
}
}

// Check PC2 (A2)
if (changed & (1 << PC2)) {
if (curr & (1 << PC2)) {
countA2++;
}
}

// Check PC3 (A3)
if (changed & (1 << PC3)) {
if (curr & (1 << PC3)) {
countA3++;
}
}

prevPortC = curr;
}

unsigned long lastReport = 0;
unsigned long lastA1 = 0, lastA2 = 0, lastA3 = 0;

void loop() {

Millis_current = millis();
if (Millis_current - Millis_cooler >= period_cooler) {
  Millis_cooler = Millis_current;
  rpm_value = rpm_value + rpm_change;
  if (rpm_value > 100) { rpm_value = 0; }
  for (int i = 0; i < 3; i++) { setPWM(i, rpm_value); }
  Serial.println(" ");
}

unsigned long now = millis();
if (now - lastReport >= 1000) { // report every 1 second
Serial.print(rpm_value); Serial.print("  ");
Serial.print(" | fan1: "); Serial.print(countA1); countA1 = 0;
Serial.print(" | fan2: "); Serial.print(countA2); countA2 = 0;
Serial.print(" | fan3: "); Serial.println(countA3); countA3 = 0;

lastReport = now;

}

//
}

Any suggestions to solve the problem?
In any case thanks in advance!

Try accessing your volatile variables in a protected block before printing them:

  noInterrupts();           // Start of critical section: disable interrupts
  localCounter = counter;   // Read the shared volatile variable
  interrupts();             // End of critical section: re-enable interrupts

May not fix your problem, but it is good practice.

Do you have a connection GND fan <-> arduino?

1 Like

DaveEvans,
Of cause, I was try to switch off interrupts when read rpm. Nothing changed.
But thanks for the reminder.

oops, I don’t have Ground between Fans (powered external PS12V) and Arduino (powered by Type-C PC)
To monitoring serial and uploading sketches, I’ve to disconnect both external power and Ground from Nano and connect Type-C to PC.

I’m not sure, if I connect only Ground from external PS (12V) to Nano, which power by Typy-C PC, and at the same time Fans connected to external PS, - Will It be safe for either the Nano and the Type-C PC controller ?

Something wrong there. You can't connect USB to Vin or to D0, D1.

Sorry about scheme.
I was try to draw a simple USB connection from PC and Nano using classic Type-C cable.
Cause when I connect type-C cable to Nano USB Port - I’ve 5V on pin VIN and Ground anyway.
D0 and D1 - of cause it’a a external Serial1, but I draw it to show the Serial communication between PC and Nano through a type-C cable.

So the computer is only connected to the Nano USB-C connector and you have nothing connected to Vin or 3.3V, is that correct?

Yeap.

Nano powered only from USB Type-C PC.
In working mode, there are no USB Type-C, and Nano powered from external PS 12V at pins VIN and Ground. But to debug code in this case I’ve to connect external LCD and write aditional code to serve it.

Then you only need to connect a wire from the 12V power supply ground to the Nano ground (GND).

1 Like

Ok, thank you.
I’ll try.

Of cause, in work mode, Ground Nano, all Fans and External PS12V connected. But I Can’t debug code without additional equipment :(

Now that you have every thing properly connected, why don't you test the code with just one fan.

Any way I need to control 3 Fans, it’s 6 pins to PS and 6 pins to Nano.
3 of them on Nano need to be on 25kHz for PWN
others 3 for read tachometers.

and…… when I connected Ground between Fans and Nano - all become stable !!!
Thanks to All so much for your help.
I understand that on this scheme I don't see direct current flow between the power supply and the PC, but I still wanted to get a professional opinion!

Now I see linear, stable pulse readings and can already calculate the actual speed.
Besides, at 100% (PWM is fully open) I see stable Fans speed!
(at the start you can see fans boost - starting air purge)

Pulse counter started on A1(A)/A2(B)/A3(C)

0   | fan1: 98 | fan2: 105 | fan3: 101
0   | fan1: 90 | fan2: 96 | fan3: 93
0   | fan1: 56 | fan2: 62 | fan3: 62
0   | fan1: 23 | fan2: 25 | fan3: 26
0   | fan1: 13 | fan2: 14 | fan3: 14
0   | fan1: 13 | fan2: 13 | fan3: 12
0   | fan1: 12 | fan2: 14 | fan3: 12
0   | fan1: 13 | fan2: 13 | fan3: 13
0   | fan1: 12 | fan2: 13 | fan3: 12
0   | fan1: 13 | fan2: 14 | fan3: 12

10   | fan1: 12 | fan2: 13 | fan3: 13
10   | fan1: 13 | fan2: 13 | fan3: 12
10   | fan1: 12 | fan2: 13 | fan3: 13
10   | fan1: 13 | fan2: 14 | fan3: 12
10   | fan1: 12 | fan2: 13 | fan3: 12
10   | fan1: 13 | fan2: 13 | fan3: 13
10   | fan1: 12 | fan2: 13 | fan3: 12
10   | fan1: 13 | fan2: 13 | fan3: 13
10   | fan1: 12 | fan2: 13 | fan3: 12
10   | fan1: 13 | fan2: 13 | fan3: 12
10   | fan1: 12 | fan2: 13 | fan3: 13

20   | fan1: 15 | fan2: 16 | fan3: 15
20   | fan1: 17 | fan2: 18 | fan3: 18
20   | fan1: 17 | fan2: 18 | fan3: 18
20   | fan1: 17 | fan2: 18 | fan3: 18
20   | fan1: 16 | fan2: 19 | fan3: 17
20   | fan1: 17 | fan2: 18 | fan3: 18
20   | fan1: 17 | fan2: 18 | fan3: 18
20   | fan1: 17 | fan2: 18 | fan3: 18
20   | fan1: 17 | fan2: 18 | fan3: 18
20   | fan1: 17 | fan2: 18 | fan3: 18

30   | fan1: 22 | fan2: 24 | fan3: 23
30   | fan1: 28 | fan2: 30 | fan3: 29
30   | fan1: 28 | fan2: 30 | fan3: 29
30   | fan1: 28 | fan2: 30 | fan3: 30
30   | fan1: 29 | fan2: 30 | fan3: 29
30   | fan1: 28 | fan2: 30 | fan3: 30
30   | fan1: 28 | fan2: 30 | fan3: 29
30   | fan1: 28 | fan2: 30 | fan3: 30
30   | fan1: 28 | fan2: 31 | fan3: 29

40   | fan1: 28 | fan2: 30 | fan3: 29
40   | fan1: 34 | fan2: 36 | fan3: 36
40   | fan1: 39 | fan2: 41 | fan3: 40
40   | fan1: 39 | fan2: 42 | fan3: 41
40   | fan1: 40 | fan2: 42 | fan3: 40
40   | fan1: 39 | fan2: 42 | fan3: 41
40   | fan1: 39 | fan2: 42 | fan3: 41
40   | fan1: 39 | fan2: 42 | fan3: 41
40   | fan1: 40 | fan2: 42 | fan3: 40
40   | fan1: 39 | fan2: 42 | fan3: 41

50   | fan1: 39 | fan2: 42 | fan3: 41
50   | fan1: 45 | fan2: 48 | fan3: 47
50   | fan1: 50 | fan2: 53 | fan3: 51
50   | fan1: 51 | fan2: 54 | fan3: 52
50   | fan1: 50 | fan2: 54 | fan3: 53
50   | fan1: 51 | fan2: 53 | fan3: 52
50   | fan1: 50 | fan2: 54 | fan3: 52
50   | fan1: 51 | fan2: 54 | fan3: 52
50   | fan1: 50 | fan2: 54 | fan3: 53
50   | fan1: 51 | fan2: 54 | fan3: 52

60   | fan1: 50 | fan2: 54 | fan3: 52
60   | fan1: 56 | fan2: 59 | fan3: 58
60   | fan1: 60 | fan2: 64 | fan3: 62
60   | fan1: 60 | fan2: 65 | fan3: 62
60   | fan1: 61 | fan2: 64 | fan3: 62
60   | fan1: 60 | fan2: 64 | fan3: 63
60   | fan1: 61 | fan2: 64 | fan3: 62
60   | fan1: 60 | fan2: 64 | fan3: 62
60   | fan1: 61 | fan2: 65 | fan3: 63
60   | fan1: 60 | fan2: 64 | fan3: 62

70   | fan1: 61 | fan2: 64 | fan3: 63
70   | fan1: 66 | fan2: 70 | fan3: 68
70   | fan1: 70 | fan2: 76 | fan3: 72
70   | fan1: 71 | fan2: 75 | fan3: 73
70   | fan1: 71 | fan2: 76 | fan3: 74
70   | fan1: 71 | fan2: 76 | fan3: 73
70   | fan1: 71 | fan2: 76 | fan3: 73
70   | fan1: 72 | fan2: 75 | fan3: 73
70   | fan1: 71 | fan2: 76 | fan3: 74
70   | fan1: 71 | fan2: 76 | fan3: 73

80   | fan1: 71 | fan2: 77 | fan3: 74
80   | fan1: 77 | fan2: 81 | fan3: 79
80   | fan1: 81 | fan2: 87 | fan3: 83
80   | fan1: 82 | fan2: 87 | fan3: 84
80   | fan1: 81 | fan2: 87 | fan3: 84
80   | fan1: 82 | fan2: 87 | fan3: 84
80   | fan1: 82 | fan2: 87 | fan3: 84
80   | fan1: 82 | fan2: 88 | fan3: 85
80   | fan1: 82 | fan2: 87 | fan3: 84
80   | fan1: 81 | fan2: 87 | fan3: 85

90   | fan1: 82 | fan2: 88 | fan3: 84
90   | fan1: 88 | fan2: 93 | fan3: 90
90   | fan1: 92 | fan2: 98 | fan3: 94
90   | fan1: 92 | fan2: 98 | fan3: 95
90   | fan1: 92 | fan2: 98 | fan3: 95
90   | fan1: 92 | fan2: 99 | fan3: 95
90   | fan1: 93 | fan2: 98 | fan3: 95
90   | fan1: 92 | fan2: 99 | fan3: 95
90   | fan1: 93 | fan2: 99 | fan3: 96
90   | fan1: 92 | fan2: 98 | fan3: 95

100   | fan1: 93 | fan2: 99 | fan3: 96
100   | fan1: 98 | fan2: 104 | fan3: 100
100   | fan1: 102 | fan2: 109 | fan3: 105
100   | fan1: 102 | fan2: 109 | fan3: 105
100   | fan1: 102 | fan2: 109 | fan3: 105
100   | fan1: 103 | fan2: 109 | fan3: 106
100   | fan1: 103 | fan2: 109 | fan3: 105
100   | fan1: 102 | fan2: 110 | fan3: 106
100   | fan1: 103 | fan2: 109 | fan3: 106
100   | fan1: 103 | fan2: 110 | fan3: 106

What beauty!!!

Don't take much to make you happy.
Have a nice day!

Thank you all so much!
Have a nice day, week, month, and 100 years ahead :slight_smile: everyone!

The solution should go to @Rintin he/she asked about the missing ground.