PWM frequency library

Hi,

thanks for your explanation. My application is about dimming power leds. Dimming is not linear at all, and for some reason, I cannot go near zero . I think it is because of integrated led driver, that must perform pwm internally. Anyway, I count get it working by using 16bits timer on an arduino Uno using LUT table to obtain linear fading. now I have to port to an arduino Mega, and your library is very "time saver" for me.

thank you.

I have working "high resolution" functions and an example, I will post it later today when I get the chance.

The code is now up. There are three new functions and an example sketch to explain how they work.
To be consistent with analogWrite(), pwmWrite() will always perform 8 bit pwm. If a higher resolution is necessary, use pwmWriteHR() instead.

void pwmWrite(uint8_t pin, uint8_t duty) 8-bit, 0 - 255
void pwmWriteHR(uint8_t pin, uint16_t duty) 16-bit, 0 - 65535

Unfortunately, resolution control is not that simple once custom frequencies come into play. If you modify the frequency on a timer, the resolution will change. The general rule of thumb is that the higher the frequency the lower the resolution. There are several variables that the SetFrequency functions wrap. They are aware of them and will mathematically determine the highest possible resolution at that given frequency. Although pwmWriteHR() accepts a 16 bit integer, it will automatically map to any timer resolution. To know whether the resolution is too low for your tolerances at a particular frequency, I have added two functions.

float GetPinResolution(uint8_t pin)
float TimerX_GetResolution() (replace X with a timer number)

These functions find the resolution in base 2/the number bits required to represent the resolution. Please note that both return floats, not ints; that is intentional. If you prefer to find the number of possible values instead of messing with binary, use TimerX_GetTop() and add 1 (which is the same thing but in base 10).

I added a sketch called PWM_lib_resolution_example to the project to demonstrate these functions and the relationship between timer frequency and resolution.
I have not been able to find enough time to exhaustively check this code for bugs, so if you find any please speak up.

Awesome library runnerup!

Wait, I can't verify my sketch as it says 'prescaler' has not been declared in the following line:

extern void		SetPrescaler_8(const int16_t timerOffset, prescaler psc);

What should I do?

Many thanks

I mean, I tried using the psalt and then it says that prescaler_alt hasn't been declared either? Am I modifying it incorrectly by just simply deleting ps_... values and the line of code in the previous reply?

Good work Ksshhs, you found a bug for me XD... but no, it was not the cause of your problem.
The library currently has a bug in the preprocessor directives. It calls the wrong internal SetPrescaller function for timer 2. I will publish version .04 to fix that right away.

I have no way of knowing what your problem is. For future reference, you should post the source you are working with, along with the type of board you are compiling for.
My guess is that you are trying to call either Timer0_SetPrescaler() or Timer2_SetPrescaler() explicitly and using the wrong syntax for the parameter.

Here is an example of setting the prescaller to 1024 on all of the mega's timers:

void setup()
{
        Timer0_SetPrescaler(ps_1024);
	Timer1_SetPrescaler(ps_1024);
	Timer2_SetPrescaler(psalt_1024);
	Timer3_SetPrescaler(ps_1024);
	Timer4_SetPrescaler(ps_1024);
	Timer5_SetPrescaler(ps_1024);
}

Same deal on the Uno and most other non megas except there are only 3 timers (0, 1, and 2) available. Attempting to use the others will give you a compiler error

For all possible prescaler parameters, look at how it it is defined in the header (you can also look at the first post in this thread):

enum prescaler
{
	ps_1	=	1,
	ps_8	=	2,
	ps_64	=	3,
	ps_256	=	4,
	ps_1024 =	5
};

//certain 8 bit timers read the CSn register differently
enum prescaler_alt
{
	psalt_1		=	1,
	psalt_8		=	2,
	psalt_32	=	3,
	psalt_64	=	4,
	psalt_128	=	5,
	psalt_256	=	6,
	psalt_1024	=	7
};

Don't worry about the values the enum elements are set to, they are used as bit masks and can only confuse humans.
Oh, and be sure to download the newest version of the library before you mess with timer 2.

pwmWriteHR is a good solution, I think.
I'll try that as soon as possible.

I'm ok it's a bit hard to deal with resolution. But things that was not possible, now are, so...

Thanks for taking on your time, it's very helpful.

Thanks for your reply runnerup, although I will have to call for your help once again as I am new to Arduino but the deadline for my project is fast approaching!! I am trying my best but am still getting compile errors, so I was hoping you could take a look at the source code I have written and with the knowledge that I am using an Arduino Uno and possibly you could spot what is going wrong!

 # include "PWM.h" 

//Initialize Motor 1
int InA1 = 8;
int InB1 = 12;
int PWM1 = 10;
int32_t frequency = 20000;

void setup() 

{  
  
   InitTimersSafe();
   bool success = SetPinFrequencySafe(PWM1, frequency);
  
   if(success){
   pinMode (InA1, OUTPUT);
   pinMode (InB1, OUTPUT);
   pinMode (PWM1, OUTPUT);}
 
}
 
void loop ()
{
  
  
  {//Motor Start Delay
       delay(1000);
       
   //Motor Direction
       digitalWrite(InA1, HIGH);
       digitalWrite(InB1, LOW); }
     
   
   //Motor Acceleration Control
       {for(int fadeValue = 0 ; fadeValue <= 76; fadeValue +=5){
         pwmWrite(PWM1, fadeValue);
       
         delay(100000); }
       }
       
   //Motor Run Time        
       delay(10000);
       
   //Motor Decceleration Control
       {for(int fadeValue = 76; fadeValue >= 0; fadeValue -=5){
          pwmWrite(PWM1, fadeValue);
          
          delay(100000); 
       }
       }  
         
   //Motor Stop Time
       delay(10000); 
  
}

Here is the error I am now getting. I have installed v4.0.

Many thanks!

 # include "PWM.h"

There shouldn't be a space between the '#' and include.

dxw00d got it, you are using the preprocessor incorrectly, but I see another bug in your code.
Pin 10 cannot be used for pwm in this library. That means:

if(success) //always skips this block, success is always false
{
   pinMode (InA1, OUTPUT);
   pinMode (InB1, OUTPUT);
   pinMode (PWM1, OUTPUT);
}//you have no else statement, you can't catch or deal with the error. Your program will continue on without dealing with this issue.

It is an avoidable property with Arduino's 8 bit timers. You can use pins 2, 3, 6, 7, 8, 11, 12, 44, 45, and 46. Pins 10 and 4 are lost for pwm output (they still work for digital input and output). In your case, since you use delay functions and the safe version of InitTimers(), you should avoid pin 5, since it is connected to timer 0.

also, look at the line

delay(100000);

that's an additional 100 seconds per iteration. Are you sure you want to do that?
And just one more thing. It appears you use a lot of curly brackets "{ ... }" where you don't need them. Generally they are used when going into scope, in the case of an if...else and for loop. Although the C language lets you put them in needless places, they will only confuse you and other readers (and potentially create local variable bugs). Your setup() and loop() have the same meaning as the following:

void setup()
{
	
	InitTimersSafe();
	bool success = SetPinFrequencySafe(PWM1, frequency);
	
	if(success){
		pinMode (InA1, OUTPUT);
		pinMode (InB1, OUTPUT);
		pinMode (PWM1, OUTPUT);
	}
	
}

void loop ()
{	
	//Motor Start Delay
	delay(1000);
	
	//Motor Direction
	digitalWrite(InA1, HIGH);
	digitalWrite(InB1, LOW);


	//Motor Acceleration Control
	for(int fadeValue = 0 ; fadeValue <= 76; fadeValue +=5){
		pwmWrite(PWM1, fadeValue);
		delay(100000);
	}

	//Motor Run Time
	delay(10000);

	//Motor Decceleration Control
	for(int fadeValue = 76; fadeValue >= 0; fadeValue -=5){
		pwmWrite(PWM1, fadeValue);
		delay(100000);
        }  
         
   //Motor Stop Time
       delay(10000); 
}

now which one is easier to read? The only differences are changes to indentation to match the scope, removing useless brackets, and moving the necessary closing brackets on to the next line (note: the indentation does look a bit exaggerated I was using a different editor that deals with tabs differently).
Hopefully this helped, good luck on your project.

This library looks to be a huge help, really appreciate the time. It really is silly that something doesn't exist like this with the stock set of libraries.

I have one issue I'm having getting started with it, that I could use some help on. I'm using a Nano v3, so basically the same thing as an Uno. Pins 5 and 6 associated with Timer 0, Pins 9 and 10 associated with Timer 1, Pins 3 and 11 associated with Timer 2.

I'm building a circuit that will manually control a Honda Insight's hybrid system. This is controlled by 3 control lines, with 2 of them being PWM signals with varying duty cycles. 1 needs to be 20kHz, and the other 2kHz. Therefor, the plan is to use Timer 1 and Timer 2.

When looking for a successful Frequency setting on the Timers, I believe I need to use Pins 10 and 3 on the respective timers. If I use 11 instead of 3, I do not get success for setting the frequency. The issue comes when I make the call to check the resolution of each pin.

Pin 10 (set at 20kHz) returns a resolution of 8.65
Pin 3 (set at 2kHz) returns a resolution of 0.00

If I reverse the pins:

Pin 10 (2kHz) returns a resolution 11.97
Pin 3 (20kHz) returns a resolution of 0.00

What would cause the 0 resolution on Timer 2?

EDIT:
Just checked and Timer 2 provides a frequency of 55555Hz after setting it at 20kHz. I played around with it and when setting it around 15kHz, it outputs almost correctly, except it returns slightly higher than set. Whenever I set it any higher than 15.7kHz, it starts reporting over double of what the set value is. For example, at 15.7kHz, it returns 31620Hz.

A pastebin of the output from your resolution example, with the pin changed to pin 3 and output from Timer2: As frequency increases, resolution will decrease...Frequency: 500 Hz Numbe - Pastebin.com

I was able to get your frequency method to report 20k, by setting this up manually:

  TCCR2A = _BV(COM2A0) | _BV(COM2B1) | _BV(WGM21) | _BV(WGM20);
  TCCR2B = _BV(WGM22) | _BV(CS21);
  OCR2A = 50;
  OCR2B = 25;

The resolution is still reported as 0, but reports possible duties of 51.

I was able to reproduce the frequency errors for timer 2 and timer 1 (55555 when set to 20000 and 31620 and 15.7K) in Atmel Studio. Sounds like a truncation issue. I was not able to reproduce the resolution errors. I ran the code test code on my Uno and Atmel Studio it looks clean.

I might have something tomorrow afternoon (east coast USA time) for the frequency problem, I will keep looking into the resolution problem.

runnerup:
I was able to reproduce the frequency errors for timer 2 and timer 1 (55555 when set to 20000 and 31620 and 15.7K) in Atmel Studio. Sounds like a truncation issue. I was not able to reproduce the resolution errors. I ran the code test code on my Uno and Atmel Studio it looks clean.

I might have something tomorrow afternoon (east coast USA time) for the frequency problem, I will keep looking into the resolution problem.

Thanks, yeah not sure what is going on with the resolution reporting. I'll keep messing with it.

I did find a nice little free logic analyzer that hooks up to an arduino to test this stuff out. I confirmed that I'm seeing 2kHz on Timer1(Pin 10), and 55.556kHz on Timer2(Pin 3). http://i.imgur.com/aRGS8.png

If anyone comes across this and wants to check their PWM signals out, try this: http://letsmakerobots.com/node/31422 . I'm running it no problems with my Mega, but needed to use version 0.9.5 of the analyzer for it to work with the Arduino plugin.

Yay integer division!
...ehem

I found the bug, I will publish the patch tomorrow afternoon after I have more time to test it.
All 8 bit timers will be set to higher than expected frequencies when set to high frequencies (go figure). I haven't actually checked myself, but according to rothnic the threshold is somewhere between 15KHz and 16KHz.
Due to the nature of the this particular bug, it should not affect PWM duty, only frequency.
Again, I have no idea what is causing that bad resolution reading, my Mega and Uno have no trouble with it.

runnerup:
Yay integer division!
...ehem

I found the bug, I will publish the patch tomorrow afternoon after I have more time to test it.
All 8 bit timers will be set to higher than expected frequencies when set to high frequencies (go figure). I haven't actually checked myself, but according to rothnic the threshold is somewhere between 15KHz and 16KHz.
Due to the nature of the this particular bug, it should not affect PWM duty, only frequency.
Again, I have no idea what is causing that bad resolution reading, my Mega and Uno have no trouble with it.

Sounds good.

Right now I have the 2kHz frequency working ok, but there is an issue with the duty cycle. This is on the 16bit Timer, so with it set at 2kHz it returns 4000 from the Timer1_GetTop call. But, if I do a pwmWrite(10, 2000), I get a Duty cycle of 81.6% in this logic analyzer. This makes me doubt the hardware a bit.

I guess in the end I can get it working with a little trial and error, worst case. Looks like pwmWrite(10, 5000) gets me pretty close to 50%.

if I do a pwmWrite(10, 2000), I get a Duty cycle of 81.6% in this logic analyzer

That's because the acceptable range for pwmWrite() is 0 - 255, or one unsigned 8 bit integer. You are truncating that int many times over with that second parameter and getting a junk duty cycle as a result.
pwmWrite() parameters are meant to mimic analogWrite() parameters. If you need more resolution, use pwmWriteHR(). It is described earlier in this thread.

This fix should solve your frequency issues, I still have no answer for the test program resolution issue. My Mega, my Uno, and Atmel studio simulators have no problem with it.

The latest version of the library as of this post is v .05. You can download it here:
https://code.google.com/p/arduino-pwm-frequency-library/downloads/list

runnerup:

if I do a pwmWrite(10, 2000), I get a Duty cycle of 81.6% in this logic analyzer

That's because the acceptable range for pwmWrite() is 0 - 255, or one unsigned 8 bit integer. You are truncating that int many times over with that second parameter and getting a junk duty cycle as a result.
pwmWrite() parameters are meant to mimic analogWrite() parameters. If you need more resolution, use pwmWriteHR(). It is described earlier in this thread.

This fix should solve your frequency issues, I still have no answer for the test program resolution issue. My Mega, my Uno, and Atmel studio simulators have no problem with it.

The latest version of the library as of this post is v .05. You can download it here:
Google Code Archive - Long-term storage for Google Code Project Hosting.

Ahh, yeah I was thinking about that while I was at work. Forgot to use the HR version. Didn't realize it was 0-255 for lower resolution frequencies as well. I'll try it out and add some results here in a bit.

Thanks again for your efforts.

Update:
Still have the 0 resolution problem on Timer2, and after looking at your code I assume there has to be some offset mismatch specific to this nano for some reason. No big deal though since I am getting the output I need: http://i.imgur.com/iwFcl.png . Both signals behaving as expected.

For those that get here looking for how to change PWM frequency for the Due, you should look here for a solution: http://arduino.cc/forum/index.php/topic,131323.msg989728.html#msg989728

Basically, only pins 6-9 on the Due board are actual HWPWM and the other pins labeled as PWM are timer pins.

You can change the frequency of the PWM pins 6-9 with the following (changes the frequency for all the pins):

PWMC_ConfigureClocks(whatever_freq_you_want * 255 , 0, VARIANT_MCK)

I'm not sure how/if individual pin frequencies can be changed.

The same can be accomplished by changing #define PWM_FREQUENCY 1000 for the pins 6-9 and #define TC_FREQUENCY        1000 for the other PWM timer pins. These constants are defined in 'variant.h'.

Hope this helps someone along the road.

Thank you runnerup for this nice piece of work!

I have some questions for you, since I really need to expand this library for the Micro/Leonardo.
So that is what I will do, but maybe you can help me in the right direction.

  • In your estimation, can I better build of of ATimerDefs or BTimerDefs?
  • Do you have any idea how I could implement the difference in structure of the new 8 - 10 - 16 bit pins and fast PWM?
  • I want to change some of the timer—pin assignments for my project. Is that something that you think will work correctly with the construction you used for the library?
  • I will find my way in the end, but can you give me some places to start looking for the registers and stuff?

Any hint is welcome.

Many thanks in advance!
Christophe aka Petit Jean