Both High frequency PWM and Servo

HiHo I am having trouble driving both a high freq PWM and a servo control. I am using Arduino Decimilia and V11 Alpha environment

I am driving pin 6 PWM at 16Khz using the following custom timer settings to get a higher freq PWM:

TCCR1A = 0x00; // sets timer control bits to PWM Phase and Frequency Correct mode TCCR1B = 0x11; // sets timer control bits to Prescaler N = 1 (0x12 is 8 scaler) ICR1 = 0x001E0; // Upper Timer Limit (ie what to count to to kick counter) = about dec 492

Works Great!

But, if I add the 'ServoTimer1' Library to control a servo on Pin 10, I get a conflict. Depending on the order of the calls to set the PWM frequency or to initialise the servo pins, either my PWM or the servo is broken (wrong frequency and duty). I assume that both functions are using the same timer and I cannot get a workable setting that will make both work.

I have tried using the 'Servo' library, but the servo pulse train only runs at 12 Hz (not the usual 50 Hz).

I understand there are three timers - 2-8 bit and 1-16bit

Questions:

  1. When I drive pin 6 PWM and use the custom settings for TCCR1A and TCCR1B, what timer am I using? Can I change this?
  2. Can I get either the PWM or the servo library to use a seperate timer?
  3. Why is the 'servo' library running at only 12 Hz?

I am using the Arduino to control a 10 Kw electric boat.

Rick Retzlaff, Saskatoon, Canada

Both the ServoTimer1 library and the PWM on pins 9 and 10 are driven by timer 1, the only 16 bit timer on the board. You can use an 8 bit timer (timer 0 or timer 1) on pin 5, 6, 3 or 11 (I think), but they will not give as precise control of the frequency. You’ll need to change the registers (e.g. TCCR1A) you use to do the custom configuring of the timer. The Servo library doesn’t use hardware timers, so it’s not as capable.

Thanks

I am OK with lower PWM resolution. Do you know what registers (for the 8 bit timers) correspond to PWM on pins 3 5 or 6 or 11. I.e. how do I tell the system to use the other timer on these pins?

Seems the easiest route is to continue to use ServoTimer1 lib and change the PWM.

Rick

Pin 3 is PD3, pin 5 is PD5, pin 6 is PD6, and pin 11 is PB3. Page 2 of the mega168 datasheet will tell you which pin is connected to which PWM output (e.g. OC0A is timer0 PWM output A, OC2B is timer2 PWM output B, etc). Then look at the datasheet section for the timer you care about to see how to use the registers.

I.e. how do I tell the system to use the other timer on these pins?

You don't. The alternate functions of these pins is to serve as a hardware PWM output driven by the associated timer. Just set timer x to generate a PWM on pin OCxA or OCxB (or both), and you will have a PWM on those pins (assuming they are set as digital outputs). OCxA and OCxB physically corresponds to specific I/O pins that cannot be changed. That's why they're called hardware PWMs.

  • Ben

Thanks!

Now I see the correspondence for the 6 PWM outouts - 2 per counter. BTW, I mispoke earlier - I am driving Pin 9 on OC1A.

So, that means I need to change TCCR0A and TCCR0B to get the right frequency. I (think) I understand the meaning of each bit in the registers and convert to HEX, but what is the corresponding HEX syntax in the statement

TCCR0A = 0x00;

are the last two digits the HEX value (00 to FF) ??

Rick

TCCR0A = 0x00;

are the last two digits the HEX value (00 to FF) ??

Yes. TCCR0A = 0xFF is the same as TCCR0A = 255.

Note that if you change the registers for timer0, millis() and delay() will no longer work as expected (millis() is driven by the timer0 overflow interrupt, and delay() relies on millis()). You might be better off using timer2 and its corresponding PWM outputs.

  • Ben

Ben, you have been more than helpful, but I have one more question

It is working on TC2A on pin 11. But, I am having a problem getting the settings for TCCR2A and TCCR2B and OCR2A to get a 16 Khz PWM (either fast or phase correct - I do not really care). I can get either 30 KHz (just the clock freq I guess) of nothing...

My best guess so far is:

TCCR2A - compare output mode, correct PWM,

COM2A - 0 1 (PWM correct phase) COM2B - 1 1 (match on upcount) WGM21 - 0 WGM20 - 1

and,

TCCR2B

FOC2 - 0 0 (PWM mode) WGM22 - 1 (Correct PWM) CS2 - 001 (no prescale)

And then I set the value of OCR2A (to DEC 128) to fine tune the PWM Freq (ie the clock will count to this value before re-starting)

-Am I interpreting this correct?? -Do I need to play with ony other registers?

Thanks, Rick

Assuming your Arduino runs off of a 16 MHz crystal, you should use a prescaler of 8, which would give you a timer2 frequency of 16 MHz / 8 = 2 MHz. If you then want a phase-correct PWM frequency of 16 kHz, you would use the formula:

PWM frequency = timer frequency / (TOP * 2) => TOP = timer frequency / (2 * PWM frequency) => TOP = 2 MHz / 32 kHz => TOP = 62.5

So in short, if you use a prescaler of 8, your phase-correct PWM will have a frequency of: 16.129 kHz when TOP is 62 15.873 kHz when TOP is 63

If you were to use a prescaler of 1, you'd need a TOP value of 500 to achieve 16 kHz, which is not possible when you are using an 8-bit timer such as timer2 (8-bit timers are limited to a TOP value of 255).

If you want an exact 16 kHz, you can use a fast PWM (instead of phase-correct), which comes from the formula:

PWM frequency = timer frequency / (TOP + 1) => TOP = (timer frequency / PWM frequency) - 1 => TOP = (2 MHz / 16 kHz) - 1 => TOP = 124

Therefore, using a prescaler of 8 and a TOP value of 124, your fast PWM will have a frequency of 16.0 kHz.

Your achieve these settings, you would want to use the following register values:

phase correct PWM with prescaler 8 and TOP value of 63 TCCR2A = 0xC1; // set COM2A1 and COM2A2 for normal PWM output on pin 11, clear COM2B1 and COM2B2 (no OC2B PWM output) TCCR2B = 0x0A; // select prescaler 8, set WGM22 and WGM20, clear WGM21 (i.e. select phase-correct PWM with TOP = OCR2A) OCR2A = 63; // set TOP to get the desired PWM frequency (15.873 kHz) OCR2B = 32; // set duty cycle to 50% (or whatever duty cycle you want)

fast PWM with prescaler 8 and TOP value of 124 TCCR2A = 0xC3; // set COM2A1 and COM2A2 for normal PWM output on pin 11, clear COM2B1 and COM2B2 (no OC2B PWM output) TCCR2B = 0x0A; // select prescaler 8, set WGM22, WGM21, and WGM20 (i.e. select fast PWM with TOP = OCR2A) OCR2A = 124; // set TOP to get the desired PWM frequency (16 kHz) OCR2B = 62; // set duty cycle to 50% (or whatever duty cycle you want)

I haven't tested this code, but if I haven't made any careless mistakes it should be what you want. If any part of it doesn't make sense, you should see if you can use the datasheet to follow what it's trying to do.

  • Ben

Ben, Thank you again for your help! I tried these blindly today with some odd results - but now I know that I am playing with the right registers and I will look harder at the docs to get the right combo of settings. I noticed that you are using OCR2B to set the duty - is this a replacemet to the analogWrite function??

Rick

Yes.

Your achieve these settings, you would want to use the following register values:

phase correct PWM with prescaler 8 and TOP value of 63 TCCR2A = 0xC1; // set COM2A1 and COM2A2 for normal PWM output on pin 11, clear COM2B1 and COM2B2 (no OC2B PWM output) TCCR2B = 0x0A; // select prescaler 8, set WGM22 and WGM20, clear WGM21 (i.e. select phase-correct PWM with TOP = OCR2A) OCR2A = 63; // set TOP to get the desired PWM frequency (15.873 kHz) OCR2B = 32; // set duty cycle to 50% (or whatever duty cycle you want)

fast PWM with prescaler 8 and TOP value of 124 TCCR2A = 0xC3; // set COM2A1 and COM2A2 for normal PWM output on pin 11, clear COM2B1 and COM2B2 (no OC2B PWM output) TCCR2B = 0x0A; // select prescaler 8, set WGM22, WGM21, and WGM20 (i.e. select fast PWM with TOP = OCR2A) OCR2A = 124; // set TOP to get the desired PWM frequency (16 kHz) OCR2B = 62; // set duty cycle to 50% (or whatever duty cycle you want)

I think I made a mistake with the red lines above. Is it possible for you to use pin 3 for your PWM output rather than pin 11? If so, you can change the red lines to:

TCCR2A = 0x21; // phase-correct PWM or TCCR2A = 0x23; // fast PWM

If you want to be able to change the TOP value of a PWM, you need to use its OCxB output rather than itx OCxA output (since the register that normally determines the duty cycle of the OCxA output is now used to determine the TOP value of the PWM). Also, I had set this up to generate an inverted PWM in my previous code sample, which means that setting OCR2B = 255 would be a 0% duty cycle and OCR2B = 0 would be a 100% duty cycle. The two lines of code above produce a normal PWM (i.e. non-inverted).

  • Ben