0

I'm simply trying to use timer 2 for tone generation and what I get is a very faint and strange tone (almost like its modulated somehow as there is a "pulsing" to it). Here's my simple code:

#include <Arduino.h>

#define SPEAKER_PORT PORTC
#define SPEAKER_BIT 2

void setup()
{
  pinMode(A2, OUTPUT);

  TCCR2A = _BV(WGM21);  // CTC mode
  TCCR2B = _BV(CS20);   // clk/1

  OCR2A = 200;  // 8-bit timer, so keep less than 256
  bitSet(TIMSK2, OCIE2A);  // enable OCR2A interrupt
}

void loop()
{
  // NOP
}

ISR(TIMER2_COMPA_vect)
{
  SPEAKER_PORT ^= _BV(SPEAKER_BIT);
}

Any obvious mistakes? Thanks for your help!

3 Answers 3

2

I tried the code on an Uno and as you say, it does sound odd.

By my calculations, the frequency is 40kHz (16MHz/(200 x 2)), which is above the threshold of hearing. The sound we are actually hearing is probably the 3rd or 5th harmonic, at a reduced intensity and perhaps subject to jitter.

I tried changing

TCCR2B = _BV(CS20); // clk/1

to

TCCR2B = _BV(CS21); // clk/8

which should mean that the frequency is now 5kHz (16MHz/8/(200 x 2)).

It sounds cleaner.

1
  • Just posted an answer with a similar conclusion, as I had cross-posted to AVRFreaks.net and got similar advice. Thanks for the help! Glad to validate that this was reproducible for you. I'll mark yours as the solution.
    – Andrew M.
    Commented Nov 14, 2019 at 2:57
1

Looks like my tone frequency was possibly just too high for the buzzer I was using, causing strange output.

Adjusting the clock divider fixed the issue. That is, I changed the following:

TCCR2B = _BV(CS20);   // clk/1

to

TCCR2B = _BV(CS22) | _BV(CS20);  // clk/128

and now I get reasonable-sounding tones.

Note that the clock divider you choose is ultimately driven by what frequency range you want to support, and the bit width of the timer. The formula is something like:

freq = 16000000 / d / n / 2

where d is the clock divider value (like 128), and n is the counter value at which to interrupt (what goes in OCR2A for instance). n must fit into the bit width of the specific timer (on ATmega328P timer 1 is 16-bits wide, and timer 2 is 8-bits wide).

Hope this is helpful for anyone else running into these issues. Thanks so much to Kartman over at AVRFreaks.net for pointing me in the right direction.

0

If you drive the speaker pin from an interrupt, you will inevitably have some jitter, as the interrupt request will sometimes fire while the CPU is processing another ISR. This can lead to one of the levels being held for longer than expected, which in turn leads to spurious frequencies in the output. Note that these are not really harmonics (harmonics always have frequencies higher than the fundamental), but rather beating between different interrupt sources. That's why you are able to hear something even though the fundamental is well above the hearing range.

If you want to have a clean PWM signal, you should instead generate it in hardware. Timer 2 for example can drive pins 11 (OC2A) and 3 (OC2B). See the bits COM2{A1,A0,B1,B0} in TCCR2A.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.