TimerFreeTone Library v1.5: Play tones without timers and therefore no conflicts

I am trying to use TimerFreeTone, and while it works it seems to choke on some duration values. I was using a pot to increase the duration and found that certain values produce a much shorter tone than that given in the duration parameter. I striped down the code to just a fixed values, it is here:-

#include 
const byte tonePin = 3;
void setup() {
pinMode(tonePin,OUTPUT);
}

void loop() {
  makeStep();
  delay(20);
}

void makeStep(){
  TimerFreeTone(tonePin,440, 400);
}

What happens is that it just outputs two cycles of the tone and then stops short. So the duration of the tone is 3.5mS not the 400 given in the duration parameter. I have tried IDE 1.6.5 and IDE 1.6.7 with the same results. I used a Uno as well as an ATtiny85 and I still get this choking. Other values work as expected, but some result in this short tone. Any ideas?

Grumpy_Mike: I am trying to use TimerFreeTone, and while it works it seems to choke on some duration values. I was using a pot to increase the duration and found that certain values produce a much shorter tone than that given in the duration parameter. I striped down the code to just a fixed values, it is here:-

#include 
const byte tonePin = 3;
void setup() {
pinMode(tonePin,OUTPUT);
}

void loop() {   makeStep();   delay(20); }

void makeStep(){   TimerFreeTone(tonePin,440, 400); }



What happens is that it just outputs two cycles of the tone and then stops short. So the duration of the tone is 3.5mS not the 400 given in the duration parameter.
I have tried IDE 1.6.5 and IDE 1.6.7 with the same results. I used a Uno as well as an ATtiny85 and I still get this choking. Other values work as expected, but some result in this short tone.
Any ideas?

I've got a guess. Try the following beta and let me know if that works:

http://www.leethost.com/link_pics/TimerFreeTone_v1.3BETA.zip

Tim

Tim, will this create inaccurate (lower) frequencies if a significant number of interrupts occur during the delayMicroseconds() calls?

For example, someone could use Serial.print() right before calling TimerFreeTone(). Or they could have another library like Servo or FreqMeasure running, which is constantly servicing interrupts from the timers. In fact, if they need this timer-free code, it's a safe bet they probably have a project already doing something useful with all the timers, which often involves interrupts.

Wouldn't it be better to read micro() at the beginning, and then in busy loops for the delays, so you can automatically adapt to interrupts consuming CPU time?

[quote author=Paul Stoffregen link=msg=2636838 date=1456622768] Tim, will this create inaccurate (lower) frequencies if a significant number of interrupts occur during the delayMicroseconds() calls?

For example, someone could use Serial.print() right before calling TimerFreeTone(). Or they could have another library like Servo or FreqMeasure running, which is constantly servicing interrupts from the timers. In fact, if they need this timer-free code, it's a safe bet they probably have a project already doing something useful with all the timers, which often involves interrupts.

Wouldn't it be better to read micro() at the beginning, and then in busy loops for the delays, so you can automatically adapt to interrupts consuming CPU time? [/quote]

That may work better in certain conditions for sure, but with additional overhead. This library is by no means accurate in frequency nor duration if there's a lot of other interrupts happening. I guess it's designed to simply "work" but with the understanding that accuracy is out.

I would use this library for simple "beep" indications when all timers are used. Not really good no matter how the duration is calculated if the user is trying to play a melody while other interrupts are triggering. The length may be a bit more accurate with your suggestion, but the frequency will still be off.

Tim

[quote author=Paul Stoffregen link=msg=2636838 date=1456622768] Tim, will this create inaccurate (lower) frequencies if a significant number of interrupts occur during the delayMicroseconds() calls?

For example, someone could use Serial.print() right before calling TimerFreeTone(). Or they could have another library like Servo or FreqMeasure running, which is constantly servicing interrupts from the timers. In fact, if they need this timer-free code, it's a safe bet they probably have a project already doing something useful with all the timers, which often involves interrupts.

Wouldn't it be better to read micro() at the beginning, and then in busy loops for the delays, so you can automatically adapt to interrupts consuming CPU time? [/quote]

Thanks for the suggestion. I did some testing and the overhead is lower in a "while" loop checking millis() than a "for" loop. I figured it would be the other way around. Anyway, I'll probably take your advice and change the way the note duration is calculated and looped.

Tim

Hi Tim,

I have this error while compiling for ATtiny13A in Arduino 1.6.9:

error: 'portModeRegister' was not declared in this scope

uint8_t *portMode = (uint8_t *) portModeRegister(digitalPinToPort(pin)); // Get the port mode register for the pin.

exit status 1 Error compiling for board Attiny 13A standalone 9.6Mhz.

No issue when I compile for Arduino Uno.

Can this library run on Attiny13A?

Thanks.

GC

GCMan: Can this library run on Attiny13A?

Sounds like whatever core you're using to compile for the ATtiny13A doesn't support port register calls. My library will work on the ATtiny13A if the core includes port register compatibility (which it appears to not).

I tried to duplicate it, but I can't find in the Arduino IDE board manager where you load the ATtiny13A core. Probably because it's not officially supported by Arduino. So, what core are you using for the ATtiny13A?

Tim

I've been using this lib and it's great, I'm using it together with a servo and rgb leds.

I'm wondering if we could control volume by software?

spicajames: I'm wondering if we could control volume by software?

A single-channel digital potentiometer would work. Or, you could try this beta release of TimerFreeTone

TimerFreeTone v1.4 - Added optional volume parameter

It's untested, but my guess is that it will work. The syntax for setting the volume is simple, just add another parameter at the end of TimerFreeTone with a volume range of 0 to 10 (0=off, 10=full volume). Below are the details:

SYNTAX: TimerFreeTone( pin, frequency, duration [, volume ] ) - Play a note on pin at frequency in Hz for duration in milliseconds. Parameters: * pin - Pin speaker is wired to (other wire to ground, be sure to add an inline 100 ohm resistor). * frequency - Play the specified frequency (should work fairly well in the 100 to 15000 Hz range). * duration - Set the duration to play in milliseconds. Range: 0 to 65535 (65.5 seconds). * volume - Optionally set the tone volume level (from 0 to 10), defaults to full volume (10).

Let me know if and how it works!

Tim

Yes, it works great!

Thanks.

spicajames: Yes, it works great!

Thanks.

I did some testing last night and changed the volume array in TimerFreeTone.cpp so it works a bit better. The line you should change in the beta is this:

uint8_t _tft_volume[] = { 255, 200, 150, 125, 100, 87, 50, 33, 22, 2 }; // Duty for linear volume control.

I'll be releasing v1.4 today so you can also download it which will include this modification.

Tim

TimerFreeTone v1.4 was released adding an optional volume parameter. Get it here:

TimerFreeTone v1.4

Tim

TimerFreeTone v1.4 and the example doesn't work for me. No compile errors. No sound at all. Using one of your other libs or standart tone works. I need no timer sound, or using timer0 (pins 5 or 6) without breaking time functions. I need pins 9,10 and 3,11 for high frequency PWM

mexus: TimerFreeTone v1.4 and the example doesn't work for me. No compile errors. No sound at all. Using one of your other libs or standart tone works. I need no timer sound, or using timer0 (pins 5 or 6) without breaking time functions. I need pins 9,10 and 3,11 for high frequency PWM

You can't use timer 0 without breaking time functions, so your only option is using TimerFreeTone. Are you using the example sketch and not getting sound output? Have you tried using a different pin (A0 is a good one to try). The example is using pin 10, which you should avoid as you're doing something with the PWM.

Anyway, my guess would be a timer conflict, and simply using a different pin would work. But, I'd need to see your sketch as well as a description of how you have things connected. I'll verify everything works later today, but I believe the example sketch works without a problem.

Tim

mexus:
TimerFreeTone v1.4 and the example doesn’t work for me. No compile errors.
No sound at all. Using one of your other libs or standart tone works.
I need no timer sound, or using timer0 (pins 5 or 6) without breaking time functions.
I need pins 9,10 and 3,11 for high frequency PWM

Seems that something changed in the latest release of the Arduino IDE that caused TimerFreeTone to totally fail. I’ve updated it to version 1.5 which corrects the problem.

Let me know if there’s any issues.

Tim

Thank you, Tim! Now it works. It doesn't break my timers and PWM outputs :). Great, just what I've needed for my project.

Shouldn’t unsigned int duration be unsinged long duration?
I replaced it in your lib. So that it can work even TCCR0B = TCCR0B & B11111000 | B00000001.
Setting clock 0 to this makes one second last 64000 instead of 1000 millis. This causes your function to overflow. If you are working with millis() than you should use unsigned long not unsigned int.

mexus: Shouldn't unsigned int duration be unsinged long duration? I replaced it in your lib. So that it can work even TCCR0B = TCCR0B & B11111000 | B00000001. Setting clock 0 to this makes one second last 64000 instead of 1000 millis. This causes your function to overflow. If you are working with millis() than you should use unsigned long not unsigned int.

Duration is set to unsigned int because a note duration of up to 65.5 seconds seemed acceptable, reduces the while loop execution time, and it saves a few bytes of programming space. It's only looking at duration for the comparison of millis()-startTime, so it should never roll over in real-world situations.

If you're messing with clock 0, all kinds of wacky stuff could happen. unsigned int is plenty long (65.5 seconds). If you're changing how long a second is, you should probably not use any libraries as all will assume certain standards exist (like a second is 1000 millis).

I see no reason to change it to unsigned long as it would slow down the while loop. Also, no one should ever be messing with timer 0 anyway and 65.5 seconds is plenty long for a note to play.

Tim

Hi, this is good stuff. I have been using on an ESP8266 and I thought you may appreciate my minor additions for a new version if of any use.

As I understand it current recommendations state portions of code that may last longer than 20 millis require a yield() or delay(0) within, to allow the ESP8266 to perform housekeeping tasks for Wifi safely.

With the above in mind I offer you a potential solution below. This does affect tonal quality but makes the library safer to use on this platform.

 while(millis() - startTime < duration) { // Loop for the duration.
 
 #ifdef __AVR__
 *pinOutput |= pinBit;    // Set pin high.
 delayMicroseconds(duty); // Square wave duration (how long to leave pin high).
 *pinOutput &= ~pinBit;   // Set pin low.
 #elif defined(ESP8266)
 GPOS = (1 << pin); // Set pin high.
 delayMicroseconds(duty); // Square wave duration (how long to leave pin high).
 GPOC = (1 << pin); // Set pin low
 yield();
 #else
 digitalWrite(pin,HIGH);  // Set pin high.
 delayMicroseconds(duty); // Square wave duration (how long to leave pin high).
 digitalWrite(pin,LOW);   // Set pin low.
 #endif
 delayMicroseconds(frequency - duty); // Square wave duration (how long to leave pin low).
 
 }

Cheers

neutralvibes: Hi, this is good stuff. I have been using on an ESP8266 and I thought you may appreciate my minor additions for a new version if of any use.

As I understand it current recommendations state portions of code that may last longer than 20 millis require a yield() or delay(0) within, to allow the ESP8266 to perform housekeeping tasks for Wifi safely.

With the above in mind I offer you a potential solution below. This does affect tonal quality but makes the library safer to use on this platform.

 while(millis() - startTime < duration) { // Loop for the duration.

#ifdef AVR *pinOutput |= pinBit;    // Set pin high. delayMicroseconds(duty); // Square wave duration (how long to leave pin high). *pinOutput &= ~pinBit;  // Set pin low. #elif defined(ESP8266) GPOS = (1 << pin); // Set pin high. delayMicroseconds(duty); // Square wave duration (how long to leave pin high). GPOC = (1 << pin); // Set pin low yield(); #else digitalWrite(pin,HIGH);  // Set pin high. delayMicroseconds(duty); // Square wave duration (how long to leave pin high). digitalWrite(pin,LOW);  // Set pin low. #endif delayMicroseconds(frequency - duty); // Square wave duration (how long to leave pin low).

}




Cheers

I would't think that would be required in this case as it's not doing anything that would prevent the normal WiFi interrupts in the middle of a tone being generated. I understand when that could be useful, but I don't think it's useful in this context. Does communications still work even when a note is playing?

Tim