[SOLVED] PWM near 2K Hz?

I'm trying to build a PWM driver out of arduino. My target is around 2KHz. I was reading the following reference:

http://playground.arduino.cc/Code/PwmFrequency

From the numbers given, it seems that you can't really get near 2KHz. 1KHz and 4KHz (3.9KHz) are doable with all pins but 2KHz is not. Is this the correct conclusion?

You can absolutely get 2kHz.

Just take over one of the timers, do it yourself, don't try to work with that guy's code. I have some serious concerns about it (a lot of the code in arduino playground is of dubious quality, sad to say)

Anyway, take over timer1, ideally - it's 16-bit so you have more options.

Put it into the WGM where the TOP value is controlled by the ICRn register, and adjust the OCRnx registers to match the intended duty cycle.

Want 2 khz? Okay, 16MHz / 2kHz = 8000, so we want 8000 clock cycles. If we set the prescaler to 1, and ICR1 to 8000, timer1 will count from 1 to 8000, and do so 2000 times per second. If you want a squarewave, you would then set the OCRnx register for the channel you want to 4000.

The appropriate section of the datasheet really lays it out for you (if you don't think what I said above makes sense, read the section on timer1 in the datasheet - that should explain things)

1 Like

Thanks DrAzzy. I'm reading the data sheet (1284P seems to have 2 16-bit timers). I just have not used timers before so I'm still reading and memorizing the acronyms.

So on a conceptual level, the PWM pins are driven HIGH/LOW automatically by hardware with a TOP value for duty cycle, correct?
So if I want to take a 16-bit timer/counter, say timer1, to do 2KHz PWM, I need to attach my code to its comparison interrupt. My code will initialize the TOP to the duty cycle and set the pin to HIGH. Whenever the comparison interrupt fires, I will flip the pin to LOW and load TOP with the remaining counts to make up 2KHz frequency. In the 8000 clock cycle case, for a 10% duty cycle, I will load 800 to the TOP and flip the pin HIGH. Then when interrupt fires, I load 7200 to the TOP and flip the pin LOW.

It's not hardware driven so I suppose I should discount some CPU cycles to run the interrupt service routine to make it close to 2KHz. Actually the exactness of 2KHz is not required. The device driven by the PWM driver didn't work with a 5KHz PWM but works with 2KHz when we tested it.

Can you provide some critique on my concept? Thanks.

So on a conceptual level, the PWM pins are driven HIGH/LOW automatically by hardware with a TOP value for duty cycle, correct?

No. The TOP value controls the frequency, the PWM pins are driven high or low at register match values to set the duty cycle.

It's not hardware driven

Why not?

If you use the hardware output on a compare match, you do not have the interrupt overhead of a compare match interrupt. You only need to use the compare match interrupt if you are not using the hardware outpins for that timer, but want to toggle a different pin.

The timer counts from 0 to TOP - in the PWM modes, at this point, it resets to 0. As it's doing so, when it passes the compare value, it switches the pin on (and when it resets to 0, it turns it off - I think there's a way to invert this too). In the default WGM that arduino uses, TOP is 255, and OCRnx is the compare value. But the 16-bit timers can be switched to a mode where it uses ICRn as the compare value - and OCRnx and ICRn are 16-bit registers...

Note that if you're accessing them inside interrupts, you need to take appropriate measures to make the access to the 16-bit registers atomic.

You're better off using hardware PWM, not twiddling the pin in an ISR, unless for some reason you're absolutely forced into it.

Have you looked at the TimerOne library capabilities?

.

1 Like

Great! Thanks guys! I'm using TimerOne library recommended by LarryD. I think with your explanations and this library source code, I will be able to understand how timer counter works. I've written a program. I'll do some test with an enclosed arduino with rotary encoder (duty cycle adjustment) tomorrow and get back here.

If you are not requiring exact timing and do not want to take a timer then running an asymmetric blink task with a period of 500 micros can get you to less than 20 micros error.. for only software.

A milli is a long time time to an Arduino running non-blocking code.

500 micros is just half of a long time. :slight_smile: Only 8000 cycles.

I have to start the device with 2.5us of pulse at whatever PWM frequency as "simmer" so accurate timing is indeed a requirement. Sorry I didn't say this earlier. What I did without the timers was using while loops as a way of timing. One empty loop is about 2.5us :slight_smile: I know it's not the right way but it got the device working which is why I want to build a better PWM control the right way this time.

I'm done with some code modification and tests. All is good using Timer1. Thanks a bunch! With this library source code, I'll be able to learn timer counter stuff with less difficulties (could play with the lib code to check my understanding).

Some pics and video to follow:

25% duty cycle @2KHz PWM

The controller:

Whole setup:

Controller closeup:

A short video:

Looks good!

.

Thanks. I used one of my open source devices I designed in 2014 otherwise the whole thing would look like hazard. Here is inside:

I didn't need the bluetooth so I got rid of it. The "FTDI" board may be a fake so I had to program it on a Linux machine :smiley: