Go Down

Topic: toneAC v1.2 - Twice the volume, higher quality, higher frequency, etc. (Read 30805 times) previous topic - next topic

GizmoGarden

Question about race conditions in toneAC2: I was seeing some odd behavior in the standard tone() function that I suspect is due to a race condition between the interrupt handler and tone() inself. The doc says you can call tone() while a previous tone is playing and it will just update the frequency, but tone() can be interrupted at any point by its own interrupt, hence the potential race. In searching the web to see if others had observed this I came across toneAC and toneAC2, and intend to use your library. I don't think I can use toneAC because I need the servo library which uses timer1 on my Uno, but toneAC2 should be just the ticket. So my question: Is there any potential race condition if toneAC2 is called while a previous tone is playing? What is the intended behavior?

The main reason I want to use toneAC2 is to take advantage of "twice the volume". I'm currently doing that with tone() using the following push-pull inverter, but software is preferable to hardware:

teckel

Question about race conditions in toneAC2: I was seeing some odd behavior in the standard tone() function that I suspect is due to a race condition between the interrupt handler and tone() inself. The doc says you can call tone() while a previous tone is playing and it will just update the frequency, but tone() can be interrupted at any point by its own interrupt, hence the potential race. In searching the web to see if others had observed this I came across toneAC and toneAC2, and intend to use your library. I don't think I can use toneAC because I need the servo library which uses timer1 on my Uno, but toneAC2 should be just the ticket. So my question: Is there any potential race condition if toneAC2 is called while a previous tone is playing? What is the intended behavior?

The main reason I want to use toneAC2 is to take advantage of "twice the volume". I'm currently doing that with tone() using the following push-pull inverter, but software is preferable to hardware:

You will need to use toneAC2 as toneAC will conflict with the servo library.  That's unfortunate as toneAC is faster, more accurate, etc.

Anyway, I don't think toneAC or toneAC2 will create a race condition because I set the new tone end before setting the new timer settings and monitor the state as well. The intended behavior if you call toneAC/toneAC2 while a current tone is playing is that the tone will simply change.  toneAC/toneAC2 resets the counter top in an overflow situation which the tone library does not do.  Also, if you specify a length, toneAC2 will run in blocking mode so it would be impossible to call toneAC2 again (although, blocking mode probably won't work with your project).

The toneAC2 library is small, fairly straight-forward, and commented in detail.  You should be able to review toneAC2.cpp to see if there's any potential issues.

Tim
My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328 systems
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My projects: http://www.DogBlocker.com/
My beer: Great Lakes Brewing Co. Lake Erie Monster

GizmoGarden

I think I do see a race condition in toneAC2 (easy to fix), but tell me what you think. The interrupt handler can call noToneAC2 and thereby change TCCR2A, TCCR2B, and _pinMask1. Suppose that a tone is just ending when toneAC2 is called to start a new one. If the tone-ending interrupt occurs between the lines in toneAC2 that set OCR2A and that disable the interrupt (TIMSK2), then some or all of _pinMask1, TCCR2A, and TCCR2B will be placed into an invalid state. If you simply move the interrupt disable somewhere before the "if (_pinMask1 == 0), the race condition is impossible.

Of course this race bug is rather unlikely to occur, but that is what makes race conditions so nasty and difficult to debug.

And of course I could be wrong here--you know both the code and the timers better than I do.

GizmoGarden

Now that I look at the code again, I think there is no race condition, but the analysis is subtle. Near the beginning of toneAC you set _tAC2_time. If this unsigned long is set correctly, it will prevent the race condition about which I was concerned in my previous post, since the interrupt handler would then not call noToneAC2. But an unsigned long is four bytes, and setting it on an 8-bit processor is not an atomic operation and can therefore be interrupted. If it is interrupted partway through setting all four bytes, the interrupt handler will run with an invalid value in _tAC2_time, and may or may not call noToneAc2. But I think either case is harmless because nothing that the interrupt handler can change has been read yet by the rest of tomeAC2. Still, I'd move the interrupt disable eariler in toneAC2.

teckel

Now that I look at the code again, I think there is no race condition, but the analysis is subtle. Near the beginning of toneAC you set _tAC2_time. If this unsigned long is set correctly, it will prevent the race condition about which I was concerned in my previous post, since the interrupt handler would then not call noToneAC2. But an unsigned long is four bytes, and setting it on an 8-bit processor is not an atomic operation and can therefore be interrupted. If it is interrupted partway through setting all four bytes, the interrupt handler will run with an invalid value in _tAC2_time, and may or may not call noToneAc2. But I think either case is harmless because nothing that the interrupt handler can change has been read yet by the rest of tomeAC2. Still, I'd move the interrupt disable eariler in toneAC2.
I set _tAC2_time early to avoid a race condition.  Also, stopping the timer earlier if tones are playing quickly can cause a "crackle" sound as we're terminating the tone before starting a new tone with too much computation between (that's why the timer stop and start are so close to each other with only a single if/else statement between them.

I don't believe having _tAC2_time as a long int could also create a race condition.  I do, however see two other cases where a race condition could happen.  One I corrected in my development code already.  The other has to do with the millis() rolling over at 0xFFFFFFFF.  It's programmed to deal with the rollover.  But, there could be a very rare case where _tAC2_time is set to a very high value and the interrupt misses it before millis() rolls over.  Say that _tAC2_time is set to 0xFFFFFFFE and the tone frequency is set to 100Hz.  ISR(TIMER2_COMPA_vect) would only be called once every 10ms and it would be very possible that millis() could wrap to 0x0 and never be greater than _tAC2_time to cause the tone to stop.  This would be highly rare and the solution would add bulk inside the ISR(TIMER2_COMPA_vect) which isn't a good idea either.

In any case.  All of what we're talking about is when we're using toneAC2 to control the length of the tone.  Another way of doing it is to not specify the duration which makes toneAC2 just play the tone forever.  Then, you could use your sketch to decide when to stop the tone.  Depending on your sketch, this may be a good way of avoiding any possible problem.

Tim
My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328 systems
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My projects: http://www.DogBlocker.com/
My beer: Great Lakes Brewing Co. Lake Erie Monster

piif

Hello,
 I work on a "home made" sound player project, using a regular 8ohms solenoid speaker and want to generate "ping" notes.
I found your library and your AC trick was exactly what I wanted to make louder sound.

But I've a problem with volume control : on full volume (50% duty cycle) the sound is ok, but when I decrease it, it becames twanging like a duck and thus "ping" effect sounds really ugly.

Is this a known problem ? I don't understand why of shorter duty cycle distorts sound so much ?

I tried another solution, by connecting speaker to a timer for frequency and other speaker end to another timer (with higher frequency) onto which I change duty cycle to simulate a variable low voltage.
Result is fine, but in this way, i can't use your AC trick thus volume is less loud

By the way, I've another question :
With a 50% duty cycle sound, the signal is like this :

(yes, my oscilloscope comes from another century ...)
Top line shows one output pin, bottom line shows other pin inverted, thus speaker receives 0 -> +5 at beginning then +5 -> 0. That's ok.


With lower volume, the signal becames like this :

thus, +5 -> 0 90% of time, then 0 -> +5 during remaining 10%

Shouldn't it be better to have something like this :

thus 0 -> 0 90% of time, then 5 -> 0 during 5%, and then 0 -> 5 during remaining 5%

I achieved this by setting OCRxA to (top / volume) and OCRxB to (top - (top / volume)), and compare flags to "normal"
Thus, A is high during the beginning of timer count, then low, then B is set to high during last steps and returns low at end.
The result sounds not exactly the same, but is still twanging, thus it doesn't solve my problem, but I'd like to have your opinion in this signal generation method ?

Thanks in advance for any help ...

teckel

Hello,
 I work on a "home made" sound player project, using a regular 8ohms solenoid speaker and want to generate "ping" notes.
I found your library and your AC trick was exactly what I wanted to make louder sound.

But I've a problem with volume control : on full volume (50% duty cycle) the sound is ok, but when I decrease it, it becames twanging like a duck and thus "ping" effect sounds really ugly.

Is this a known problem ? I don't understand why of shorter duty cycle distorts sound so much ?

I tried another solution, by connecting speaker to a timer for frequency and other speaker end to another timer (with higher frequency) onto which I change duty cycle to simulate a variable low voltage.
Result is fine, but in this way, i can't use your AC trick thus volume is less loud

By the way, I've another question :
With a 50% duty cycle sound, the signal is like this :

(yes, my oscilloscope comes from another century ...)
Top line shows one output pin, bottom line shows other pin inverted, thus speaker receives 0 -> +5 at beginning then +5 -> 0. That's ok.


With lower volume, the signal becames like this :

thus, +5 -> 0 90% of time, then 0 -> +5 during remaining 10%

Shouldn't it be better to have something like this :

thus 0 -> 0 90% of time, then 5 -> 0 during 5%, and then 0 -> 5 during remaining 5%

I achieved this by setting OCRxA to (top / volume) and OCRxB to (top - (top / volume)), and compare flags to "normal"
Thus, A is high during the beginning of timer count, then low, then B is set to high during last steps and returns low at end.
The result sounds not exactly the same, but is still twanging, thus it doesn't solve my problem, but I'd like to have your opinion in this signal generation method ?

Thanks in advance for any help ...
The "volume" is a bit of a hack for sure.  It changes the duty cycle, but it's not ideal.  Basically, the goal of toneAC was to create a "push/pull" for the speakers using the built-in timers of the ATmega which would make it high accuracy and very fast.  The duty cycle allowed me to adjust the volume so I added it to the library.

I don't have an oscilloscope to test what the output of toneAC is.  But for me, it does change the volume and doesn't sound like a duck.  I'm using only piezo transducers, so maybe that's the difference?  I would be concerned about your modification if the pins are ever both high.

Honestly, I wouldn't use the volume option in toneAC.  It "works" but not well.  It's a bonus only.  toneAC was really designed to be driven at 100% to produce a higher sound output, high accuracy, and the ability to do high frequencies.

The reason it still doesn't sound right with your modification is probably because the pins are out of sync.  Maybe if I had an oscilloscope I could mess around with this.  But I'm kind of out of luck until such time.

Tim
My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328 systems
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My projects: http://www.DogBlocker.com/
My beer: Great Lakes Brewing Co. Lake Erie Monster

piif

Thanks for your answer.
I've to find a piezo speaker to do the test, but i fear sound will not be loud enough.
My goal is to make a door bell, thus it have to be eared from everywhere in my house ...

teckel

Thanks for your answer.
I've to find a piezo speaker to do the test, but i fear sound will not be loud enough.
My goal is to make a door bell, thus it have to be eared from everywhere in my house ...

The best solution to this is using a simple amp (a transistor will do).  The output of the ATmega just isn't enough to drive a cone speaker at type of a loud volume.

Tim
My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328 systems
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My projects: http://www.DogBlocker.com/
My beer: Great Lakes Brewing Co. Lake Erie Monster

piif

The best solution to this is using a simple amp (a transistor will do).  The output of the ATmega just isn't enough to drive a cone speaker at type of a loud volume.

Tim
Hi,
  that's what i did, but to drive the speaker with toneAC, I needed to amplify signal both ways, thus i have to use a H-bridge.

As my volume troubles were not solved, I finally used my "second timer" idea, and connected it to the "enable" pin of a H-bridge component. I tried to build my own with Darlingto transistors, but never achieve something working (i think i will never understand how to deal with those weird components !) thus i took a 754410

The final result is working and installed on my door since today !
I will post detail soon (in french, sorry, but source code is english commented)
Thanks for your help

teckel

Hi,
  that's what i did, but to drive the speaker with toneAC, I needed to amplify signal both ways, thus i have to use a H-bridge.

As my volume troubles were not solved, I finally used my "second timer" idea, and connected it to the "enable" pin of a H-bridge component. I tried to build my own with Darlingto transistors, but never achieve something working (i think i will never understand how to deal with those weird components !) thus i took a 754410

The final result is working and installed on my door since today !
I will post detail soon (in french, sorry, but source code is english commented)
Thanks for your help
If using an amp you don't need to use toneAC.  The standard tone or one of my other tone libraries will work (like NewTone).

Tim
My hardware: Arduino, Teensy 2.0, Teensy 3.2 & custom ATmega328 systems
My libraries: NewPing, LCDBitmap, toneAC, toneAC2, NewTone, TimerFreeTone
My projects: http://www.DogBlocker.com/
My beer: Great Lakes Brewing Co. Lake Erie Monster

piif


freakolowsky

Hey, not sure if i'm doing something wrong or what ...

i'm using a Micro (ATMega32U4) and have a buzzer connected on pins 9&10. Positive pin on the speaker has a 100 Ohm resistor on it ...

first of all volume (at 10) is equal if not even lower than when using default tone. If i switch pins the volume gets even lower.

As i said ... not sure what i'm doing wrong.

PS: i also have a switch on D12(pullup) and LCD 16x2 on D2-D8

any ideas.

mariemarolleflyvop

Hey, I was wondering, is there a way to make ToneAC work with a sensor and one LED?
Fx. you activate the sensor, the LED will turn on and a sound from ToneAC will play. I already did this with the normal Arduino settings and a speaker but to be honest am I not quiet so skilled at Arduino.
I want to use ToneAC because of the improved volume.
Thanks a lot :-)

Go Up
 


Please enter a valid email to subscribe

Confirm your email address

We need to confirm your email address.
To complete the subscription, please click the link in the email we just sent you.

Thank you for subscribing!

Arduino
via Egeo 16
Torino, 10131
Italy