[Solved] Increasing PWM frequency and resolution

Hi... I thought I'd make another thread because I am severely confused!

I'm trying to increase the frequency of a PWM pin's output (let's say this pin uses Timer1), and increase the resolution to 10-bit. I've spent the last couple of hours trying to make sense of the datasheet, and lots of old threads and blog entries about how to go about this, and I've seen so many slightly different methods that I don't know what to do any more!

At the moment, this is what I have in setup():

  TCCR1B = TCCR1B & 0b11111000 | 0x01; // 31250 Hz
  TCCR1A = 0x03; // 10 bit?

Is this correct?

And if the pin is now 10-bit, does this mean I could analogWrite values from 0 to 1023? That's what I'm trying to do... :sweat_smile:

Thanks,
+-

If you re-program the timer to get 10-bit PWM you can't use analogWrite() anymore. You have to write to the timer registers directly.

johnwasser:
If you re-program the timer to get 10-bit PWM you can't use analogWrite() anymore. You have to write to the timer registers directly.

Wow, I think that's a bit too much for me!

I found this shortly after I made this thread: 16bit PWM Function - #3 by graynomad - Programming Questions - Arduino Forum

If I used that instead of TCCR1A = 0x03;, would I then be able to use analogWrite()?

Thank you :slight_smile:

Here's a problem. Other things aside:

void analogWrite(uint8_t pin, int val)
{
	// We need to make sure the PWM output is enabled for those pins
	// that support it, as we turn it off when digitally reading or
	// writing with them.  Also, make sure the pin is in output mode
	// for consistenty with Wiring, which doesn't require a pinMode
	// call for the analog output pins.
	pinMode(pin, OUTPUT);
	if (val == 0)
	{
		digitalWrite(pin, LOW);
	}
	else if (val == 255)
	{
		digitalWrite(pin, HIGH);
	}
	else
	{

First, analogWrite takes a 16-bit value. So far so good. But it has a specific test for 255. That's not so good. My testing confirms that if you try something like this:

analogWrite (9, 254);
analogWrite (9, 255);   // fully on
analogWrite (9, 256);

Then you get the glitch on exactly 255. And that was after adding:

TCCR1A = 0x03;

This seems to work better:

  OCR1A = timerVal; // set pwm duty

Further testing under way ...

Interesting. What if I changed val in analog_wiring.c to 1023, and changed the little timer bit in wiring.c?

But I take it I will be able to change OCR1A just as I would use analogWrite() in loop()? That sounds okay to me.

This seems to work OK in a sense:

#include <SPI.h>
#include "pins_arduino.h"

void setup ()
{
  SPI.begin ();
  pinMode (SS, OUTPUT);
  // PWM, Phase Correct, 10-bit and set D9 pin correctly
  TCCR1A = _BV (WGM10) | _BV (WGM11) | _BV (COM1A1);
  pinMode (9, OUTPUT);
}  // end of setup

int timerVal = 240;

void loop ()
  {
  // debugging
  digitalWrite (SS, LOW);
  SPI.transfer (highByte (timerVal));
  SPI.transfer (lowByte (timerVal));
  digitalWrite (SS, HIGH);
  // end debugging

  OCR1A = timerVal; // set pwm duty
  
  delay (20);
  timerVal++;
  }  // end of loop

I'm using SPI to see with the logic analyzer exactly what the value is at a given moment.

At the moment when we have sent out 257, we get a pulse width of 2.0545 out of 8.1819 which gives:

2.0545 / 8.1819 * 1023 = 256.88

So, close enough to a duty cycle of 257 / 1023. That seems to work then.

You will note that the frequency has dropped down to 122 Hz. This would be because of the longer overall cycle time (counting up to 1023 rather than 255). You could bump that back up by changing the prescaler.

You can do that by changing setup to:

void setup ()
{
  SPI.begin ();
  pinMode (SS, OUTPUT);
  // PWM, Phase Correct, 10-bit and set D9 pin correctly
  TCCR1A = _BV (WGM10) | _BV (WGM11) | _BV (COM1A1);
  TCCR1B &= ~7;  // clear prescaler
  TCCR1B |= _BV (CS11);  // prescaler of 8
  pinMode (9, OUTPUT);
}  // end of setup

The extra two lines change the prescaler from 64 to 8, multiplying the frequency by 8. Now I measure a frequency of 978 Hz which is roughly 122 Hz * 8.

plusminus_:
Interesting. What if I changed val in analog_wiring.c to 1023, and changed the little timer bit in wiring.c?

You could do that. But what I showed is just as easy.

plusminus_:
But I take it I will be able to change OCR1A just as I would use analogWrite() in loop()? That sounds okay to me.

Yes, as in the code above.

Thank you so much for this! If I wanted a prescaler of 1 (or rather, no prescaler), would I put CS10 in the brackets, instead of CS11?

Sorry, I just found this: TCCR1B |= ~(_BV(CS12) | _BV(CS11) | _BV(CS10));

Actually, I think my first suggestion is correct!

Yes. My earlier line cleared the existing prescaler, so this effectively would do the same thing:

  TCCR1B &= ~7;  // clear prescaler
  TCCR1B |= _BV (CS10);  // prescaler of 1

Thank you :slight_smile:

Sorry for lingering here beyond the [Solved] word but I thought this library provides a way to manipulate timer1 for PWM (arbitrary period and 10bit):

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

I am myself just starting with this so don't know too much about it.

liudr:
Sorry for lingering here beyond the [Solved] word but I thought this library provides a way to manipulate timer1 for PWM (arbitrary period and 10bit):

Arduino Playground - HomePage

I am myself just starting with this so don't know too much about it.

Hi!

I came across this library yesterday, and at the time of reading, I just couldn't make sense of the example, so I decided not to use it. But it makes sense to me now, and seems to be a slightly nicer way of what Mr. Gammon did. :smiley:

However, in the last hour I've realised something awful... with 10-bit PWM, and with the timer frequency set to 31250 Hz (but with the PWM frequency being a quarter of this), if I wrote a value of 1 (out of 1024, approximately 0.1% duty cycle), the pulse has a duration of 0.000128s, if my calculations are correct, and my low-pass filter does not have a settling time anywhere near that. I don't think it's very likely that a value of 1 would ever need to be written, but I'm considering going back to 8-bit, or trying 9-bit, because of this! =(

edit: duh, I should reduce the frequency, shouldn't I?

liudr:
Sorry for lingering here beyond the [Solved] word but I thought this library provides a way to manipulate timer1 for PWM (arbitrary period and 10bit):

Arduino Playground - HomePage

That library looks nice. I must admit that the question was asked in terms of register manipulation, and that's how I answered. Plus I like to understand what all those registers do.

edit: duh, I should reduce the frequency, shouldn't I?

Not sure I understand that question, but 10 bit PWM sounds pretty precise. And you are right, if you write a 1 there is a long gap before the next 1. I suppose it depends on what you need.

I'll need to read the library doc once more. I think I am not understanding how it works entirely. Will report back with more understanding once I have some :slight_smile:

Sorry, I'm confusing myself again.

I came across a document about PWM filters: http://www.proaxis.com/~wagnerj/PWMfil/PWM%20Filters.pdf and on page 12 there's a graph of what happens to the filter output when there is a narrow pulse. I was concerned that because a single pulse of 10-bit PWM is narrower than that of 8-bit PWM, and with a high frequency, that the filter would not work correctly. But having thought about it some more, I think it will still work.