Question about timers in Arduino AVR tone() - Page 1
Forum section: electronics / general

author: MLXXXp
author_rank: Frequent Contributor
timestamp: June 17, 2016, 09:40:58 pm
content: I've been looking at the AVR version of theArduino 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:// Assume the sketch only calls tone(pin, frequency, duration)volatile long timer3_toggle_count; // count for the tone duration in half cyclestoneBegin(pin) {// test here: if toneBegin() hasn't been previously been called for this pin: {TCCR3A = 0;TCCR3B = 0; // this will stop the timerbitWrite(TCCR3B, WGM32, 1); // set "Clear Timer on Compare Match (CTC)" modebitWrite(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/64ocr = 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/16toggle_count = 2 * frequency * duration / 1000;OCR3A = ocr; // set to a count of one half cycletimer3_toggle_count = toggle_count; // set duration in half cyclesbitWrite(TIMSK3, OCIE3A, 1); // enable interrupts}}// Timer interrupt service routineISR(TIMER3_COMPA_vect){if (timer3_toggle_count != 0) {*timer3_pin_port ^= timer3_pin_mask; // toggle the pintimer3_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}}

author: dannyf
author_rank: Super Contributor
timestamp: June 18, 2016, 12:00:16 am
content: QuoteYeah:Code:bitWrite(TCCR3B, WGM32, 1); // set "Clear Timer on Compare Match (CTC)" modeNot very well written as it assumes that bits of WGM3x have certain values - which may or may not be true.

author: MLXXXp
author_rank: Frequent Contributor
timestamp: June 18, 2016, 12:59:32 am
content: Quote from: dannyf on June 18, 2016, 12:00:16 amThat 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()?Quote from: dannyf on June 18, 2016, 12:00:16 amThe rest of the WGM3x bit are cleared in theTCCR3A = 0;andTCCR3B = 0;statements above, so they will be in a known state.

author: dannyf
author_rank: Super Contributor
timestamp: June 18, 2016, 01:24:54 am
content: once the toggle count reaches zero, the pin is no longer flipped in the isr.

author: MLXXXp
author_rank: Frequent Contributor
timestamp: June 18, 2016, 02:01:51 am
content: Quote from: dannyf on June 18, 2016, 01:24:54 amTrue, thepinisn't flipped, but doesn't the timer'scountercontinue 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.

author: dannyf
author_rank: Super Contributor
timestamp: June 18, 2016, 02:06:31 am
content: both are possible.

author: MLXXXp
author_rank: Frequent Contributor
timestamp: June 18, 2016, 02:21:52 am
content: Quote from: dannyf on June 18, 2016, 02:06:31 amSo 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 0when a tone starts[...]?"is not correct?

author: MLXXXp
author_rank: Frequent Contributor
timestamp: June 18, 2016, 10:38:36 am
content: I've edited my initial post to clarify that it's the start time of the tone, not the duration, that could vary.

author: westfw
author_rank: Super Contributor
timestamp: June 18, 2016, 07:50:09 pm
content: QuoteI 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!)

author: Buriedcode
author_rank: Super Contributor
timestamp: June 18, 2016, 09:01:07 pm
content: 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.

author: MLXXXp
author_rank: Frequent Contributor
timestamp: June 18, 2016, 09:24:02 pm
content: Quote from: westfw on June 18, 2016, 07:50:09 pmI'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.Quote from: westfw on June 18, 2016, 07:50:09 pmAs 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.

author: MLXXXp
author_rank: Frequent Contributor
timestamp: June 18, 2016, 09:38:27 pm
content: Quote from: Buriedcode on June 18, 2016, 09:01:07 pmYou 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.