Arduino UNO PWM duty-cycle 100% before analogWrite(pin, 255)

Description of project:
I want to controll the speed of a PC fan by varying the duty-cycle on the PWM/4th pin of the fan. I will be testing to see if I can do this with an Arduino UNO and an external power supply for the fan, see attached wiring diagram “Schematic”. For testing I am using a fan from Intel, E97378-001. Reading the Tach pulses from the fan on Arduino pin 2 to determine speed.

I am using the library “Arduino PWM Frequency Library”, version from post #126 here, to achieve an output frequency of 25kHz on pin 3. This is the frequency that should be used, Extract from wikipedia:
The control signal is a square wave operating at 25 kHz, with the duty cycle determining the fan speed.

To change the speed I use Serial Monitor from Arduino IDE to send numbers 0 to 255 followed by an S. The number sent will be used in the analogWrite function. The “S” is just to determine when I am done sending the number.

Now to the problem:
I am able to control the fan speed (I have an external RPM measurement tool to verify my readings.), but what I think is weird is that I am at a 100% duty-cycle when I analogeWrite(3, 40), the range is currently 0 to 40, why not 0 to 255?
I tried measureing the duty-cycle when the frequency is 500Hz, then the range is 0 to 250. but it doesn’t go all the way up to 255, or to say it in another way, the duty-cycle is 100% between 250 and 255 when the frequency is 500Hz and between 40 and 255 when the frequency is 25kHz , why is that?

To measure the duty-cycle I connected pin 3 to pin 2, see attached code DutyCycle.ino and the schematic DutyCycleRead.png.

To control fan and read it’s speed see attached code Fan_control.ino and schematic Schematic.png

Note: I did try googeling a lot, but I guess I didn’t know how to formulate myself since I couldn’t find anything about it.

DutyCycle.ino (1.48 KB)

DutyCycleRead.PNG

Fan_control.ino (1.65 KB)

So, what is the problem?

PaulS: So, what is the problem?

Now I have updated my post with the remaining information, but I'll write the problem here too.

I expected to be able to control the fan speed by varying the duty-cycle from 0 to 100% this is achieved. The thing is that I was hoping that I could adjust it not as rough as I am now. Now the duty-cycle is at 100% when I analogWrite(3, 40), and 0% when i analogWrite(3, 0). That's a range from 0 to 40, i was expecting the range to be 0 to 255.

Now the duty-cycle is at 100% when I analogWrite(3, 40), and 0% when i analogWrite(3, 0).

No. The duty cycle is 4000/255, or 15.68%, when you analogWrite(3, 40), and 0% when you analogWrite(3, 0).

Now, analogWrite(3, 40) MIGHT correspond to a fan speed of 100% while writing 0 to the pin stops the fan.

i was expecting the range to be 0 to 255.

Talk to the fan.

PaulS:
No. The duty cycle is 4000/255, or 15.68%, when you analogWrite(3, 40), and 0% when you analogWrite(3, 0).

Now, analogWrite(3, 40) MIGHT correspond to a fan speed of 100% while writing 0 to the pin stops the fan.
Talk to the fan.

Could you explain this to me then, here is the output from the Serial Monitor.
Here I have pin 2 and 3 on the Arduino UNO connected togheter directly as shown in DutyCycleRead.png using the code from DutyCycle.ino (I might have commented out the part that sets the frequency on pin 3 to 25kHz in the attached code, here I have uncommented it.)

Here you can see what the frequency on pin 3 is before and after it was changed, then you get readings of the ON and OFF time and the frequency. You can see that I change the analogWrite value also.
ON and OFF is given in microseconds.
The period of a 25kHz signal is 40 microseconds, adding High and Low gives you 40 microseconds.
When I analogWrite(3, 39), it’s almost always ON , when analogWrite(3, 40) it’s always ON , at least accordig to my readings. If I analogWrite more than 40 I get the same readings showing it’s always ON.

I don’t have an oscilloscope at home to verify the duty-cycle with, planned on checking when I get back to work.

Serial Monitor output:

Frequency on pin 3 changed = False
Frequency on pin 3 = 500 Hz
Frequency on pin 3 changed = True
Frequency on pin 3 = 25000 Hz
analogWrite pin 3 = 10
High = 11 Low = 30 Hz = 23288
High = 10 Low = 31 Hz = 24995
analogWrite pin 3 = 1
High = 2 Low = 39 Hz = 24970
High = 0 Low = 40 Hz = 24996
High = 2 Low = 39 Hz = 24971
analogWrite pin 3 = 15
High = 16 Low = 25 Hz = 24996
High = 16 Low = 25 Hz = 24968
High = 16 Low = 26 Hz = 24994
analogWrite pin 3 = 25
High = 26 Low = 15 Hz = 24994
High = 25 Low = 16 Hz = 24968
High = 26 Low = 15 Hz = 24994
analogWrite pin 3 = 35
High = 35 Low = 6 Hz = 24968
High = 35 Low = 6 Hz = 24996
High = 35 Low = 6 Hz = 24971
analogWrite pin 3 = 39
High = 40 Low = 0 Hz = 24996
High = 39 Low = 2 Hz = 24971
High = 40 Low = 0 Hz = 24996
High = 40 Low = 0 Hz = 24996
analogWrite pin 3 = 0
High = 0 Low = 0 Hz = 10572
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
analogWrite pin 3 = 40
High = 0 Low = 0 Hz = 1
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
analogWrite pin 3 = 20
High = 20 Low = 20 Hz = 24971
High = 21 Low = 21 Hz = 24994
High = 21 Low = 21 Hz = 24994
High = 20 Low = 21 Hz = 24969
analogWrite pin 3 = 100
High = 0 Low = 0 Hz = 15979
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
analogWrite pin 3 = 150
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
analogWrite pin 3 = 200
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
analogWrite pin 3 = 255
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0
High = 0 Low = 0 Hz = 0

At 25000 Hz, the cycle of the PWM pin will be 40 microseconds. Part of that time, the pin will be on, and part of the time the pin will be off. The duty cycle refers to the portion of time that the pin is on of the whole cycle.

The thing that you seem to be missing is that you are writing to a pin that goes to the fan, and reading a pin that comes from the fan. Yet, you are expecting that the fan output will be somehow exactly in sync with the input to the fan. I'd never make that assumption.

I believe that you are running into an issue with the library. For pin 3 it is using 8 bit Timer 2 with a prescaler of 8 using a pwm mode which counts up and down to OCR2A. It uses an OCR2A value of 40 for a total of 80 counts at .5us per count so a cycle is 40us to give 25KHz frequency.

Your pwm resolution will only be from 0 to 40 as the match for OCR2B(pin3) can not exceed OCR2A.

You could try using pin 9 or 10 which uses Timer 1 and the PWM library may set it up with a prescaler of 1.

You can also try pin using the TimerOne library (TimerOne.h) available through the Library manager. This library will give you a pwm duty cycle setting from 0 to 1023. There is a library example for a 25KHz fan.

Did you try default PWM. Most 4-pin fans control just fine with default PWM (use pin 5 or 6). The 25kHz standard is mostly used to make the fan silent, but I never noticed any difference. Most fans need at least a PWM value of 50-75 for minimum speed, so speed range should be ~70-255. Run the fan at full speed on startup for a few seconds, before turning it down. Leo..

cattledog:
I believe that you are running into an issue with the library. For pin 3 it is using 8 bit Timer 2 with a prescaler of 8 using a pwm mode which counts up and down to OCR2A. It uses an OCR2A value of 40 for a total of 80 counts at .5us per count so a cycle is 40us to give 25KHz frequency.

Your pwm resolution will only be from 0 to 40 as the match for OCR2B(pin3) can not exceed OCR2A.

You could try using pin 9 or 10 which uses Timer 1 and the PWM library may set it up with a prescaler of 1.

You can also try pin using the TimerOne library (TimerOne.h) available through the Library manager. This library will give you a pwm duty cycle setting from 0 to 1023. There is a library example for a 25KHz fan.

When I find the time I’ll investigate these OCR things. Think I might understand now, but probably don’t.

I did as you suggested and tried setting it up with Timer1 and use pin 9. The range is now 0 to 255, but at 254 the duty cycle is ~80%, at 255 it’s 100%.

Just to clarify things, I am verifying the duty cycle by connecting pin 9 of the arduino to pin 2, reading it directly as described earlier when i used pin 3 and 2.

I also tried the TimerOne library which gave perfect controll of the duty cycle, and it’s easy to use. Measuring the duty cycle gives exactly what I was expecting. I used part of the example code that you mentioned, Timer1.pwm(fanPin, (dutyCycle / 100) * 1023);Like this the range is 0-100.

When controlling the fan:
Setting the duty cycle to 100% sets the fan to 100%, when setting the duty cycle to 80%, the fan is at 75%, and the lower I get, the more off it gets. Setting the duty cycle to 20% sets the fan to about 1-2%, below that the fan is at 0% of it’s range. This might (most likely) have something to do with the fan of course. Even so I’m pretty happy with the results, and I’ll be using it like this.

Wawa:
Did you try default PWM.
Most 4-pin fans control just fine with default PWM (use pin 5 or 6).
The 25kHz standard is mostly used to make the fan silent, but I never noticed any difference.
Most fans need at least a PWM value of 50-75 for minimum speed, so speed range should be ~70-255.
Run the fan at full speed on startup for a few seconds, before turning it down.
Leo…

I did try default PWM when I first started with this, but I had wired it incorrectly. I tried again since you suggested it and I do have just as fine controll over the duty cycle as with the TimerOne library using the default PWM.

When it comes to controlling the fan it’s as you say, with the fan I am using, the range is 55-255. And it works without any problem. When looking at duty cycle precentage as I do with the TimerOne library, both gives the same results.
I will stick with 25kHz for now, since I like following standards.

As a last test:
After testing out these three methods I figured that I’d take another look at how I did things to begin with when I first posted this. Looking at things percentagewise, It gives the same results (makes sense), but it’s too rough, compared to the other methods.
So it seems like the fan I have accepts a duty cycle ranging from around 20%,25% to 100%, even if the frequency is ~490Hz or ~25kHz.

Thank you for your help.

MrJonathan: When I find the time I'll investigate these OCR things. Think I might understand now, but probably don't.

I think it's a matter of establishing the actual situation involving time between counts (eg. 0.5 microsecond between counts), and the number of bits of the counting register (eg. 8 bits). These things determine PWM frequency and PWM resolution.

If you use the dedicated pins on the UNO (according to arduino documentation) that support 490 Hz (such as pin 3), or other pins that support 980 Hz (pin 5 or 6), then using a multimeter to measure the voltage on the pin output (using 'DC voltage setting') while varying the PWM level will usually show that the correct maximum voltage occurs at PWM level 255.

https://www.arduino.cc/reference/en/language/functions/analog-io/analogwrite/

Otherwise, if some other PWM frequency is going to be used, then it will be necessary to make sure that the software (code or library) is doing what it is meant to do --- otherwise it might not do what we would like it to do. It's not always the fault of us users. Sometimes, it's to do with the lack of clear documentation that comes with a library. Or sometimes no documentation or not enough documentation.

Hi,
If you are using PWM.h library, then this is an example from it.

/*

 Mimics the fade example but with an extra parameter for frequency. It should dim but with a flicker 
 because the frequency has been set low enough for the human eye to detect. This flicker is easiest to see when 
 the LED is moving with respect to the eye and when it is between about 20% - 60% brighness. The library 
 allows for a frequency range from 1Hz - 2MHz on 16 bit timers and 31Hz - 2 MHz on 8 bit timers. When 
 SetPinFrequency()/SetPinFrequencySafe() is called, a bool is returned which can be tested to verify the 
 frequency was actually changed.
 
 This example runs on mega and uno.
 */

#include <PWM.h>

//use pin 11 on the Mega instead, otherwise there is a frequency cap at 31 Hz
int led = 9;                // the pin that the LED is attached to
int brightness = 0;         // how bright the LED is
int fadeAmount = 5;         // how many points to fade the LED by
int32_t frequency = 35; //frequency (in Hz)

void setup()
{
  //initialize all timers except for 0, to save time keeping functions
  InitTimersSafe(); 

  //sets the frequency for the specified pin
  bool success = SetPinFrequencySafe(led, frequency);
  
  //if the pin frequency was set successfully, turn pin 13 on
  if(success) {
    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);    
  }
}

void loop()
{
  //use this functions instead of analogWrite on 'initialized' pins
  pwmWrite(led, brightness);

  brightness = brightness + fadeAmount;

  if (brightness == 0 || brightness == 255) {
    fadeAmount = -fadeAmount ; 
  }     
  
  delay(30);      
}

Notice it does not use analogWrite…

Have you tried this example?

I hope I have the right library.

Tom… :slight_smile:

TomGeorge:
Hi,
If you are using PWM.h library, then this is an example from it.

/*

Mimics the fade example but with an extra parameter for frequency. It should dim but with a flicker
because the frequency has been set low enough for the human eye to detect. This flicker is easiest to see when
the LED is moving with respect to the eye and when it is between about 20% - 60% brighness. The library
allows for a frequency range from 1Hz - 2MHz on 16 bit timers and 31Hz - 2 MHz on 8 bit timers. When
SetPinFrequency()/SetPinFrequencySafe() is called, a bool is returned which can be tested to verify the
frequency was actually changed.

This example runs on mega and uno.
*/

#include <PWM.h>

//use pin 11 on the Mega instead, otherwise there is a frequency cap at 31 Hz
int led = 9;                // the pin that the LED is attached to
int brightness = 0;        // how bright the LED is
int fadeAmount = 5;        // how many points to fade the LED by
int32_t frequency = 35; //frequency (in Hz)

void setup()
{
  //initialize all timers except for 0, to save time keeping functions
  InitTimersSafe();

//sets the frequency for the specified pin
  bool success = SetPinFrequencySafe(led, frequency);
 
  //if the pin frequency was set successfully, turn pin 13 on
  if(success) {
    pinMode(13, OUTPUT);
    digitalWrite(13, HIGH);   
  }
}

void loop()
{
  //use this functions instead of analogWrite on ‘initialized’ pins
  pwmWrite(led, brightness);

brightness = brightness + fadeAmount;

if (brightness == 0 || brightness == 255) {
    fadeAmount = -fadeAmount ;
  }   
 
  delay(30);     
}



Notice it does not use analogWrite......

Have you tried this example?

I hope I have the right library.

Tom.... :)

I used analogWrite(pin, value);, I just guessed at that time that since analogeWrite(); was how we normally pwm, that’s how we always do it. As soon as I have time I’ll try using pwmWrite(pin, value);, maybe it’ll give better results than before.

I checked in the post for that library, runnerup actually writes about pwmWrite(pin, value); in the post, that’s how much attention I pay.

Thank you :slight_smile: