Creating tones using timer1

Go To Last Post
13 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm trying to use timer1 on the ATmega328 for waveform generation, but there's a glitch when it starts. This program demonstrates:

unsigned char Scale[] = { 239, 213, 190, 179, 159, 142, 127, 119 };

void setup()
{
  // Timer1 in Fast PWM mode counting from 0 to OCR1A; 64 x prescaler
  TCCR1A = 1<<COM1A0 | 3<<WGM10;
  bitSet(DDRB, 1);
}

void loop()
{
  TCCR1B = 3<<WGM12 | 3<<CS10;
  for (int note=0; note<=7; note++)
  {
    OCR1A = (Scale[note]-1);
    delay(250);
  }
  TCCR1B = 3<<WGM12 | 0<<CS10;
  delay(4000);
}

The first time it plays the scale the first note is missing. Thereafter the whole scale is played.

I've tried several things without success. Any suggestions why this is happening?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
void loop()
{
  TCCR1B = 3<<WGM12 | 3<<CS10;
  for (int note=0; note<=7; note++)
  {
    while (TCNT1 > 50) ;      //wait until safe to change.
    OCR1A = (Scale[note]-1);
    delay(250);
  }
  TCCR1B = 3<<WGM12 | 0<<CS10;
  delay(4000);
}

If you change OCR1A from 239 to 219 when TCNT1 is 220, TCNT1 must count up to 65535 and then wrap around until 219.

CTC mode changes OCR1A immediately. The PWM modes cater for the possible glitch by not changing OCR1A until TOP (or BOTTOM). i.e. when safe.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You need to set the first OCR1A value before setting TCCR1B:TCCR1A (or more specifically, before you set the WGMxx bits).

Quote:
If you change OCR1A from 239 to 219 when TCNT1 is 220, TCNT1 must count up to 65535
Not in the mode the OP has chosen.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Try scale[note] since note starts at zero?

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Try scale[note] since note starts at zero?


Say what?

OP is >>not<< negative-indexing the array; OP is adjusting the calculated period values in the table to account for timer counting starting at zero.

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yep. You are correct. Thought it looked like scale[note-1]. Sorry.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
You need to set the first OCR1A value before setting TCCR1B:TCCR1A (or more specifically, before you set the WGMxx bits).

Thanks - I've just tried that but it doesn't seem to do the trick.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've found the solution. Adding TCNT1H = 0; TCNT1L = 0; to setup() did the trick:

void setup() 
{ 
  // Timer1 in Fast PWM mode counting from 0 to OCR1A; 64 x prescaler 
  TCCR1A = 1<<COM1A0 | 3<<WGM10; 
  TCNT1H = 0;
  TCNT1L = 0;
  bitSet(DDRB, 1); 
} 

Presumably timer1 was already running, and had passed my first compare value before I set OCR1A.

Thanks for your replies; they set me thinking in the right direction.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Presumably timer1 was already running
Which would mean that Arduino is using the timer for something else which means you've destroyed that functionality by accessing the timer registers directly (which to me is a major flaw in Arduino).

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

bitSet(DDRB, 1);

I thought there was a discussion about this not too long ago. Doesn't the pin need to be made an output before connecting it to the timer? Hunting...
Discussed here, and the link therein:
https://www.avrfreaks.net/index.p...

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Making the pin an output before configuring timer1 doesn't help.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
Doesn't the pin need to be made an output before connecting it to the timer?
No it does not. The DDR bit controls whether or not the pin is an output, not what the source of that output will be.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Koshchi wrote:
Quote:
Doesn't the pin need to be made an output before connecting it to the timer?
No it does not. The DDR bit controls whether or not the pin is an output, not what the source of that output will be.

LOL -- you participated at least a bit in the discussions I linked to above...

I've always done "what works" with timer-attached pins. Stop the timer; disconnect the pin; set the desired state when the PWM or other output signal is "idle".

So, if I'm doing e.g. PWM and want to pause it, I could just make the pin an input; the timer would keep running and the pin would float? With it still connected, will it still recognize the PORT value and pull up the pin?

My confusion on this is that we know that when the pin is connected, timer stopped or not, changes to PORT aren't honored.

(just a curiosity to me...)

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.