Controlling PWM frequency from 1-50hz -- possible?

Hello,

I am new to Arduino, but learning quickly. I was wondering if there is a way in code (sketch) to vary the PWM timer on a pin (let's say pin 2) on a Mega 2560 using the registers/bitshifting to vary it between 1-50hz for a LED flashing project. I have it working via delay() and some math right now but there just has to be a better way to do it.

In other words, change the PWM frequency to lets say pulse at 1hz varied up to 50-60hz based on values sent to the timer/divider.

Thanks to anyone that can answer this question. I've been looking around and found a few articles on how to manipulate but I guess I can't get my brain around it as they are written.

The tone function will get you from ~31 Hz to a frequency well beyond 50 Hz. If you feel like "diving in at the deep end", try modifying tone to support lower frequencies.

How accurate does the frequency need to be?

As accurate as possible. Tone, is that one of the examples or libraries?

AH Just found it.. (tone).... DOH. Let me see if that can help me! Thank you for the information!

You can get quite precise frequencies by playing with the timer hardware. You could use the CTC mode (clear timer on count) to simply get a specific frequency (the count combined with the prescaler gives you that).

PWM lets you vary the duty cycle, you may or may not need that. Some of the PWM modes let you specify your own "top" of the count, which effectively controls the frequency.

Let me look at your link.... I just want to flash a LED at 1-50hz without using a loop... The tone() function will do it if I can figure out how to get it to use the 16bit timer... but your way may be even better.

Thank you for taking the time to reply! It's most appreciated!

This sketch flashes an LED without using a loop (you see loop is empty):

const byte LED = 9;

void setup() {
  pinMode (LED, OUTPUT);
  
  // set up Timer 1
  TCCR1A = _BV (COM1A0);  // toggle OC1A on Compare Match
  TCCR1B = _BV(WGM12) | _BV(CS10) | _BV (CS12);   // CTC, scale to clock / 1024
  OCR1A =  4999;       // compare A register value (5000 * clock speed / 1024)
}  // end of setup

void loop() { }

That was from this page:

The frequency is controlled by OCR1A (and the prescaler).

What frequency would that flash at? I'm going to try it. I just got to a real computer to read your page.. VERY informative stuff!!!

It does appear to work, I'm pretty tired and trying to get my brain around the math involved..

OCR1A = 4999; // compare A register value (5000 * clock speed / 1024)

Not sure on how that formula works... i'll have to read it when I am fully awake.. the flashing gets faster the lower the number but not sure how to calculate what value i want with that formula above.

I am guessing I have to use Timer 1 for this as none of the others (on the Mega2560) have the same 62500 frequency.... or am I mistaken?

Either way, you have helped me tremendously, thank you. :grin:

mattj949:
What frequency would that flash at? I'm going to try it. I just got to a real computer to read your page.. VERY informative stuff!!!

The clock period is 1/16000000, in other words 62.5 nS.

The prescaler of 1024 means the timer ticks once every 1024 * 62.5 nS, namely every 64000 nS (64 uS). Since we are counting up to 5000 the period of the timer is 5000 * 64 uS = 320000 uS or 320 mS. Thus the frequency is 1/0.320 namely 3.125 Hz.

mattj949:
I am guessing I have to use Timer 1 for this as none of the others (on the Mega2560) have the same 62500 frequency.... or am I mistaken?

Depending on your accuracy requirements, any timer will probably work.

However the lowest frequency with the 8-bit timers with their maximum prescaler would be 61 Hz (see table on that page). The 16-bit timer lets you make a much longer period and therefore a lower frequency.

I got it to work, however... in my code I am reading a value coming in from serial and I put all the values for the Hz I want into a array and I am simply assigning the numbers based in the array to the register:

These are in setup():

TCCR1A = _BV (COM1A0); // toggle OC1A on Compare Match
TCCR1B = _BV(WGM12) | _BV(CS10) | _BV (CS12); // CTC, scale to clock / 1024

This is way down in my loop():

flickSet = flickValues[serialdata]; (serialdata is a unsigned int from 0 - 50) flickValues = an array, which has 51 elements 0-50

OCR1A = flickSet;

The problem is, it works OK, but if I try to move the values from lower 1hz toward higher Hz too fast, it starts choking, then catches up. If I am going the other way.. from Higher Hz -> Lower Hz, it doesn't seem to happen.

Update: When it "Stalls" it either turns the output on for approx. 3s or off for approx 3s.. solid. So the light will stay on for 3s, or just be off for 3s then it goes to the current Hz as set. Do I need to be resetting the timer every time I change the value or maybe disable the interrupt?

Any suggestions? I'm trying lots of different things.

mattj949:
I got it to work, however... in my code I am reading a value coming in from serial and I put all the values for the Hz I want into a array and I am simply assigning the numbers based in the array to the register:

These are in setup():

TCCR1A = _BV (COM1A0); // toggle OC1A on Compare Match
TCCR1B = _BV(WGM12) | _BV(CS10) | _BV (CS12); // CTC, scale to clock / 1024

This is way down in my loop():

flickSet = flickValues[serialdata]; (serialdata is a unsigned int from 0 - 50) flickValues = an array, which has 51 elements 0-50

OCR1A = flickSet;

The problem is, it works OK, but if I try to move the values from lower 1hz toward higher Hz too fast, it starts choking, then catches up. If I am going the other way.. from Higher Hz -> Lower Hz, it doesn't seem to happen.

Update: When it "Stalls" it either turns the output on for approx. 3s or off for approx 3s.. solid. So the light will stay on for 3s, or just be off for 3s then it goes to the current Hz as set. Do I need to be resetting the timer every time I change the value or maybe disable the interrupt?

Any suggestions? I'm trying lots of different things.

You will probably need to disable the timer (probably by disabling interrupts) when making large changes.

Not 100% sure about how to disable the timer... but I think it may be a overflow problem.. If I move very slow, it does not happen. If i try to change values too fast: it just freezes (full on or full off).

Probably what is happening is this ... you tell it to count to 100, but then when it is at 90 you change your mind and say "count to 50". But it has already passed 50, so it counts all the way up to 65535 and then up to 50, hence the big delay.

You can stop the counter by turning off the clock source:

TCCR1B = 0;

Then start it again and set up your new count amount. It might also help to reset the counter yourself, eg.

TCNT1 = 0;

If you do that you may not need to stop it.

I did find out how to reset the timer... TCNT1 = 0;

Made it alot better, but when it resets, it puts the pin to full ON for a very short time, making the transition from one Hz to the next look rather un-smooth.. hmm.

It's getting better!.......... the problem is the values for the OCR1A are pretty far apart, so it has to reset, put the new value in, etc.. so it's like stepping up from 2-5hz or 2-3 3-4 4-5 and skipping all in between... just trying to make it look more smooth now.. the pausing is gone with the reset.. setting TCCR1B = 0 didn't seem to help at all. Maybe I should just do a on-the-fly calculation of the new values instead of using preset ones from a array for OCR1A ?

I have the device (Darlington Array) I am controlling on Pin 11 (Timer 1 A) (Mega 2560) ..

Try setting TCNT1 to OCR1A/2

That made it even better, thank you for the suggestion!