Controlling Several 4 Pin PWM Fans on MEGA 2560 - Newbie but with working code

Hi All,

I'm a complete Arduino Newbie trying to do a project controlling a 120mm 4 pin case fan using an Arduino MEGA 2560. Reading this forum and some other links I found I have put together the following code:

// Declare variables 
unsigned long previousRPMMillis;
unsigned long previousMillis;
float RPM;
int state = 0;
unsigned long interval = 3000;
volatile unsigned long pulses=0;
unsigned long lastRPMmillis = 0;

// Specify the input and output pins
const int readPin = 2;  // Read the 
const int fanPin = 8;

void countPulse() {
  // just count each pulse we see
  // ISRs should be short, not like
  // these comments, which are long.
  pulses++;
}

unsigned long calculateRPM() {
  unsigned long RPM;
  noInterrupts();
  float elapsedMS = (millis() - lastRPMmillis)/1000.0;
  unsigned long revolutions = pulses/2;
  float revPerMS = revolutions / elapsedMS;
  RPM = revPerMS * 60.0;
  lastRPMmillis = millis();
  pulses=0;
  interrupts();

  return RPM;
}

// Set the duty-cycle
void analogWrite25k(int value)
{
    OCR4C = value;
}

// Set up the pins - first we want to set the duty-cycle to be at 25,000 Hz for PWM Control
// next we want to set up the RPM reading.
void setup()
{   
    TCCR4A = 0;
    TCCR4B = 0;
    TCNT4  = 0;

    // Mode 10: phase correct PWM with ICR4 as Top (= F_CPU/2/25000)
    // OC4C as Non-Inverted PWM output
    ICR4   = (F_CPU/25000)/2;
    OCR4C  = ICR4/2;                    // default: about 50:50
    TCCR4A = _BV(COM4C1) | _BV(WGM41);
    TCCR4B = _BV(WGM43) | _BV(CS40);

    Serial.begin(115200);
    pinMode(readPin,INPUT_PULLUP); // Set pin to read the Hall Effect Sensor
    attachInterrupt(digitalPinToInterrupt(readPin), countPulse, RISING); // Attach an interrupt to count

    // Set the PWM pin as output.
    pinMode( fanPin, OUTPUT);
}

void loop()
{
    int w = Serial.parseInt();
    if (w>0) {
        analogWrite25k(w);
        Serial.println(w);
        state = w;
    }

    if (millis() - previousMillis > interval) {
    Serial.print("RPM=");
    Serial.print(calculateRPM());
    Serial.print(F(" @ PWM="));
    Serial.println(state);
    previousMillis = millis();  
    }
  
}

So from this I have learned that I need to work with the low level timers of the board to achieve a 25kHz PWM control rate. I have also learned that each PWM has an attached timer, and that PWM pins have to be used to read the RPM.

My questions are as follows:

  1. Please help me to understand the following block of code. Also, how do I do this for 2 more fans at the same time - which timers and pins do I use?
    TCCR4A = 0;
    TCCR4B = 0;
    TCNT4  = 0;

    // Mode 10: phase correct PWM with ICR4 as Top (= F_CPU/2/25000)
    // OC4C as Non-Inverted PWM output
    ICR4   = (F_CPU/25000)/2;
    OCR4C  = ICR4/2;                    // default: about 50:50
    TCCR4A = _BV(COM4C1) | _BV(WGM41);
    TCCR4B = _BV(WGM43) | _BV(CS40);
  1. How do I keep my RPM measurements from jumping around?

  2. Do I really need to use PWM pins for measuring the RPM?

Thanks!!

Welcome from another newbie :slight_smile:

I believe most of your questions can be answered here, including the jumping rpm and which pins can be used for what.

You can attach as many fans as you have usable pins. Your mega has 6 that can handle the interrupt....

Thanks for getting back to me! Unfortunately this does not work for me. A couple things I'm seeing:

  1. This library is not working output a PWM signal.
  2. Its not counting the RPM at all.
  3. Only pins 2 and 3 read RPM using my program. The other interrupt pins are reading wildly wrong.

MadProgrammer123:
Thanks for getting back to me! Unfortunately this does not work for me. A couple things I'm seeing:

  1. This library is not working output a PWM signal.

Actually it is using a library that you need to download. Using this you can just use 1 command to set pwm frequency, and the library will handle all the rest.

MadProgrammer123:
2. Its not counting the RPM at all

Yes it is. Video time 11-15minutes explain how speed is measured using interrupts without costing you processing time. I have not understood interrupts myself fully, but they work for all tasks that need to run at the side without blocking your program until they are finished.....
The jitering rpm can probably be solved with the schmitt trigger described around 14 minutes.

MadProgrammer123:
Only pins 2 and 3 read RPM using my program. The other interrupt pins are reading wildly wrong.

You need to configure each pin to read what you want. Not all pins are capable to accept all configurations. There are digital and analog pins, there are pins capable of reading i2c comm data....whatever you want it to do, you need to tell arduino what you want.
If you want the pins to read interrupt data, you need to tell them. They do not know themselves.

As a general hint: i always try youtube first. It isually has 10-15 minute tutorials that guide you through basic steps (setting up ports, adding libraries, basic coding, processor-specific hints). Many arecuseless, it takes some time to find useful ones. The forum is great to find people helping you modify or interconnect the modules.