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

I've already written 3 alternative tone libraries (toneAC, toneAC2 & NewTone). However, a user asked for a way to create tones without using any timers as he had conflicts with both timer 1 and timer 2. This seemed like a perfect opportunity to confuse people further by creating a 4th library that did essentially the same thing, generate sound. So, I wrote up sample code that evolved into the TimerFreeTone library. As a bonus, it works with all ATmega/ATtiny AVR microcontrollers as well as the Arduino Due, Teensy 3.x and other ARM-based microcontrollers equally well. As a second bonus, it allows you to set a volume level per note.

Download TimerFreeTone v1.5

v1.5 - Fixed problem with latest release of the Arduino IDE which caused the library to totally stop functioning. Adjusted note duration to not fail on timer rollover. Now delays for note duration when frequency or volume are zero.

v1.4 Added optional volume parameter.

v1.3 Fixed problem with long tone play durations. Changed the way the note duration is calculated from a suggestion by Paul Stoffregen.

v1.2 calculates duration differently for higher tone accuracy and smaller code size.

v1.1 added a few small changes (after I got around to actually tested the library first-hand with an Arduino).

#include <TimerFreeTone.h>

#define TONE_PIN 10 // Pin you have speaker/piezo connected to (be sure to include a 100 ohm resistor).

int melody[] = { 262, 196, 196, 220, 196, 0, 247, 262 };
int duration[] = { 250, 125, 125, 250, 250, 250, 250, 250 };

void setup() {
  for (int thisNote = 0; thisNote < 8; thisNote++) { // Loop through the notes in the array.
    TimerFreeTone(TONE_PIN, melody[thisNote], duration[thisNote]); // Play thisNote for duration.
    delay(50); // Short delay between notes.
  }
}

void loop() {}

TimerFreeTone Library home page

Tim

imho more choices is a good thing, especially if this new lib is on the same quality level of previous work!

New version of the Timer FreeTone library released.

Download TimerFreeTone v1.1

This release was actually tested on an Arduino, so I'm sure it works now. I also fixed/changed a few things. It generates code that's a bit smaller as well. Let me know how it works for you!

Tim

hi Tim,

thanks for the library, I am trying to play a tone before a for loop but when i place it there in the code the tone repeats and the code freezes

any ideas?

#include <TimerFreeTone.h>
#include <SevenSeg.h>


SevenSeg disp1(30,31,32,33,34,35,36);
SevenSeg disp2(40,41,42,43,44,45,46);

const int numOfDigits1 = 3;
const int numOfDigits2 = 2;
int digitPins1[numOfDigits1] = {2,3,4};
int digitPins2[numOfDigits2] = {5,6};

void setup() {
  disp1.setDigitPins(numOfDigits1,digitPins1);
  disp1.setDigitDelay(2000);
  disp1.setTimer(1);
  disp1.startTimer();
 
  disp2.setDigitPins(numOfDigits2,digitPins2);
  disp2.setDigitDelay(2000);
  disp2.setTimer(2);
  disp2.startTimer();
  
  disp1.setCommonCathode();
  disp2.setCommonCathode();
 

}

void loop() {
  disp2.write(23);
  TimerFreeTone(13, 500, 500);
 for (int i = 180; i >= 0; i--){
  
            disp1.writeClock(i);
            delay(100);
 }
 
}

ISR(TIMER1_COMPA_vect){
  disp1.interruptAction();
}

ISR(TIMER2_COMPA_vect){
  disp2.interruptAction();
}

teckel:
New version of the Timer FreeTone library released.

Download TimerFreeTone v1.1

This release was actually tested on an Arduino, so I'm sure it works now. I also fixed/changed a few things. It generates code that's a bit smaller as well. Let me know how it works for you!

Tim

evancleary:
hi Tim,

thanks for the library, I am trying to play a tone before a for loop but when i place it there in the code the tone repeats and the code freezes

any ideas?

#include <TimerFreeTone.h>

#include <SevenSeg.h>

SevenSeg disp1(30,31,32,33,34,35,36);
SevenSeg disp2(40,41,42,43,44,45,46);

const int numOfDigits1 = 3;
const int numOfDigits2 = 2;
int digitPins1[numOfDigits1] = {2,3,4};
int digitPins2[numOfDigits2] = {5,6};

void setup() {
  disp1.setDigitPins(numOfDigits1,digitPins1);
  disp1.setDigitDelay(2000);
  disp1.setTimer(1);
  disp1.startTimer();

disp2.setDigitPins(numOfDigits2,digitPins2);
  disp2.setDigitDelay(2000);
  disp2.setTimer(2);
  disp2.startTimer();
 
  disp1.setCommonCathode();
  disp2.setCommonCathode();

}

void loop() {
  disp2.write(23);
  TimerFreeTone(13, 500, 500);
for (int i = 180; i >= 0; i--){
 
            disp1.writeClock(i);
            delay(100);
}

}

ISR(TIMER1_COMPA_vect){
  disp1.interruptAction();
}

ISR(TIMER2_COMPA_vect){
  disp2.interruptAction();
}

So, you're saying if you comment out JUST this line: TimerFreeTone(13, 500, 500); it hangs? Could it be that it's always hanging but you don't realize it? In other words, the audio indicator is telling you it's hanging, but it's also hanging without TimerFreeTone you just can't tell because there's no audio indication?

TimerFreeTone doesn't use timer interrupts. But, it does use delayMicroseconds. If SevenSeg.h messes with the Arduino timer in such a way that it messes up delayMicroseconds, it could cause a problem.

Have you tried contacting the SevenSeg.h authors? TimerFreeTone is very simple, the entire code is here to review:

https://bitbucket.org/teckel12/arduino-timer-free-tone/src/f56f6255785f167adf28193ac78c1f62f2aa8405/TimerFreeTone.cpp?at=master

It does nothing fancy, sets the pin to output mode, calculates the square wave duration (frequency) and note duration (loops) and runs a "for" statement for however many loops are required to plan the tone for the specified length of time.

My guess is that SevenSeg is messing with the delayMicroseconds command and that's causing the problem.

Tim

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 <TimerFreeTone.h>
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 <TimerFreeTone.h>

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?

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

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