Author Topic: Question about timers in Arduino AVR tone()  (Read 1999 times)

0 Members and 1 Guest are viewing this topic.

Offline MLXXXp

  • Regular Contributor
  • *
  • Posts: 243
  • Country: ca
Question about timers in Arduino AVR tone()
« on: June 17, 2016, 09:40:58 pm »
I've been looking at the AVR version of the Arduino tone() function. Specifically, for an ATmega32u4 using timer/counter 3.

My question is:
Is there something that causes the timer to start counting from 0 when a tone starts, or is it possible that the first half cycle of the tone will be wrong (which will also affect the duration)? It looks to me like the timer will continue to run between tone() calls, so its count value could be non-zero when tone() starts a new tone.

EDIT:
Clarification: Since the pin is kept low between tones, the time during the first half cycle will be silent. The length of the first half cycle is the only thing that will vary. Therefore, the duration of the tone is precise. It's the time between calling tone() and the tone actually starting that could vary.

Here is a simplified breakdown of the code for timer 3 (hope I didn't miss anything). Not much of the pin handling is included because it doesn't affect how the timer operates. Also, the code to handle infinite durations is removed.

Code: [Select]
// Assume the sketch only calls tone(pin, frequency, duration)

volatile long timer3_toggle_count; // count for the tone duration in half cycles

toneBegin(pin) {
  // test here: if toneBegin() hasn't been previously been called for this pin: {
    TCCR3A = 0;
    TCCR3B = 0;  // this will stop the timer
    bitWrite(TCCR3B, WGM32, 1); // set "Clear Timer on Compare Match (CTC)" mode
    bitWrite(TCCR3B, CS30, 1);  // set clock with no prescaling
  //}
}

tone(pin, frequency, duration) {
  long toggle_count = 0;
  uint32_t ocr = 0;

  toneBegin(pin);

  // two choices for the 16 bit timers: ck/1 or ck/64
  ocr = F_CPU / frequency / 2 - 1;
  prescalarbits = 0b001;
  if (ocr > 0xffff) {
    ocr = F_CPU / frequency / 2 / 64 - 1;
    prescalarbits = 0b011;
  }

  TCCR3B = (TCCR3B & 0b11111000) | prescalarbits; // set ck/1 or ck/16

  toggle_count = 2 * frequency * duration / 1000;

        OCR3A = ocr; // set to a count of one half cycle
        timer3_toggle_count = toggle_count; // set duration in half cycles
        bitWrite(TIMSK3, OCIE3A, 1); // enable interrupts
  }
}

// Timer interrupt service routine
ISR(TIMER3_COMPA_vect)
{
  if (timer3_toggle_count != 0) {
    *timer3_pin_port ^= timer3_pin_mask; // toggle the pin
    timer3_toggle_count--; // decrement the duration count
  }
  else {
    bitWrite(TIMSK3, OCIE3A, 0); // disable timer interrupt
    *timer3_pin_port &= ~(timer3_pin_mask);  // keep pin low after stop
  }
}
« Last Edit: June 18, 2016, 10:37:09 am by MLXXXp »
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8229
  • Country: 00
Re: Question about timers in Arduino AVR tone()
« Reply #1 on: June 18, 2016, 12:00:16 am »
Quote
Is there something that causes the timer to start counting from 0 when a tone starts,

Yeah:

Code: [Select]
   bitWrite(TCCR3B, WGM32, 1); // set "Clear Timer on Compare Match (CTC)" mode

Not very well written as it assumes that bits of WGM3x have certain values - which may or may not be true.
================================
https://dannyelectronics.wordpress.com/
 

Offline MLXXXp

  • Regular Contributor
  • *
  • Posts: 243
  • Country: ca
Re: Question about timers in Arduino AVR tone()
« Reply #2 on: June 18, 2016, 12:59:32 am »
Yeah:
Code: [Select]
   bitWrite(TCCR3B, WGM32, 1); // set "Clear Timer on Compare Match (CTC)" mode

That sets CTC mode, which means the timer is reset to 0 when it reaches the OCR3A value. However, once it's reset wouldn't it start counting up again immediately, even after the interrupt is disabled? What prevents it from continuing to cycle between calls to tone()?

Not very well written as it assumes that bits of WGM3x have certain values - which may or may not be true.

The rest of the WGM3x bit are cleared in the TCCR3A = 0;  and TCCR3B = 0; statements above, so they will be in a known state.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8229
  • Country: 00
Re: Question about timers in Arduino AVR tone()
« Reply #3 on: June 18, 2016, 01:24:54 am »
once the toggle count reaches zero, the pin is no longer flipped in the isr.
================================
https://dannyelectronics.wordpress.com/
 

Offline MLXXXp

  • Regular Contributor
  • *
  • Posts: 243
  • Country: ca
Re: Question about timers in Arduino AVR tone()
« Reply #4 on: June 18, 2016, 02:01:51 am »
once the toggle count reaches zero, the pin is no longer flipped in the isr.

True, the pin isn't flipped, but doesn't the timer's counter continue to run? If so, the next time tone() is called, the timer's counter will be at some value between 0 and the previous value of OCR3A. If the timer isn't at zero when the new tone() function finishes, then the time it takes to flip the pin the first time in the ISR will be unknown.

It's even possible that if the new value written to OCR3A is lower than the previous value, and the timer is above the new value, the counter will have to count all the way up to 0xFFFF, then wrap to 0 and continue up to OCR3A before the pin is flipped.
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8229
  • Country: 00
Re: Question about timers in Arduino AVR tone()
« Reply #5 on: June 18, 2016, 02:06:31 am »
both are possible.
================================
https://dannyelectronics.wordpress.com/
 

Offline MLXXXp

  • Regular Contributor
  • *
  • Posts: 243
  • Country: ca
Re: Question about timers in Arduino AVR tone()
« Reply #6 on: June 18, 2016, 02:21:52 am »
both are possible.

So you're now saying that the answer to my initial question is "Yes, the exact duration of any tone after the first one is unpredictable because the time before the pin is flipped for the first time (at the start of the tone) is unknown."?

And, your initial "Yeah" reply to that question:
"Is there something that causes the timer to start counting from 0 when a tone starts [...]?"
is not correct?
 

Offline MLXXXp

  • Regular Contributor
  • *
  • Posts: 243
  • Country: ca
Re: Question about timers in Arduino AVR tone()
« Reply #7 on: June 18, 2016, 10:38:36 am »
I've edited my initial post to clarify that it's the start time of the tone, not the duration, that could vary.
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 3145
  • Country: us
Re: Question about timers in Arduino AVR tone()
« Reply #8 on: June 18, 2016, 07:50:09 pm »
Quote
Is there something that causes the timer to start counting from 0 when a tone starts, or is it possible that the first half cycle of the tone will be wrong
I didn't see anything, and I couldn't tell from the datasheet whether any of those mode changes actually resets the count register (which probably means that they don't.)

Why do you care, and ... what do you expect to happen?  What about the last cycle of the previous tone?  Presumably the most correct behavior would involve waiting for the next "zero crossing" of the counter, but that would interfere with the "length" of the notes unless you were really careful to always make the note length a multiple of the wavelength.  All of which is likely irrelevant for pseudo-music; do you know what note transitions actually look like on real musical instruments?  (me neither!)
 

Offline Buriedcode

  • Super Contributor
  • ***
  • Posts: 1226
  • Country: gb
Re: Question about timers in Arduino AVR tone()
« Reply #9 on: June 18, 2016, 09:01:07 pm »
If the pin is low when the tone routine is called, and the timer isn't cleared then yes, there will be a variable delay between calling the routine and the first rising edge on that pin - but all edges will have the same period.

Why can't you just add 'TMRCNT3 = 0'  when it sets up the CTC mode if it bugs you so much? 

Even if it did create a single low-high-low pulse that wasn't the period (or rather half the period) of the intended tone I don't think you'll hear it.
 

Offline MLXXXp

  • Regular Contributor
  • *
  • Posts: 243
  • Country: ca
Re: Question about timers in Arduino AVR tone()
« Reply #10 on: June 18, 2016, 09:24:02 pm »
Why do you care, and ... what do you expect to happen?

I'm working on a more specialised tone library and thought I'd use Arduino Tone as a base, or at least for guidance. I wanted to fully understand how it worked and in attempting to do so, my question arose. I just wanted to make sure I hadn't overlooked something in the datasheet.

As you say, it's not likely to be noticeable for just generating music. However, if you wanted to use the technique for a different purpose, such as generating a frequency pulse a fairly precise time close to a trigger for a specific duration, it might be something that you should be aware of.

I think the problem could mostly be solved by setting all the CS3x bits to 0 just before disabling the interrupt in the timer ISR. This would stop the timer's counter. Since being in CTC mode would reset the counter to 0 at the same time as generating the interrupt, the counter would be stopped at (or close to) 0. The tone() function already sets the bits for the proper prescaler (/1 or /64), so the counter would start running again from 0 at that point. You would probably also have to stop the timer's counter and clear it manually in noTone().

 Because I'm just using it for music, like tone() is intended, I'm probably not going to worry about it. Again, I just wanted to make sure I understand the code and datasheet correctly.

What about the last cycle of the previous tone?

As long as the previous tone isn't halted early by noTone() or by starting a new tone before the previous one completes, the last cycle would be the proper length because the proper number of counts would have occurred between interrupts. Whether that last cycle was actually silence would depend if the duration count started out odd or even. You could round the duration count to always be odd or even if you desired.

Thanks for taking the time to look into this and reply.
 

Offline MLXXXp

  • Regular Contributor
  • *
  • Posts: 243
  • Country: ca
Re: Question about timers in Arduino AVR tone()
« Reply #11 on: June 18, 2016, 09:38:27 pm »
Why can't you just add 'TMRCNT3 = 0'  when it sets up the CTC mode if it bugs you so much?

You could, or you could stop the timer's counter in the ISR when it would be 0, as I mentioned in my previous post (which I was composing when you posted yours).

It doesn't bug me at all. Sorry, I should have mentioned that fact initially. I just wanted to make sure I understood the code and datasheet properly, and hadn't missed anything that would cause the timer to start from 0.

Thanks for the suggestion.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf