/* Tone.cpp A Tone Generator Library Written by Brett Hagman This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Version Modified By Date Comments ------- ----------- -------- -------- 0001 B Hagman 09/08/02 Initial coding 0002 B Hagman 09/08/18 Multiple pins 0003 B Hagman 09/08/18 Moved initialization from constructor to begin() 0004 B Hagman 09/09/26 Fixed problems with ATmega8 0005 B Hagman 09/11/23 Scanned prescalars for best fit on 8 bit timers 09/11/25 Changed pin toggle method to XOR 09/11/25 Fixed timer0 from being excluded 0006 D Mellis 09/12/29 Replaced objects with functions 0007 B Cook 10/05/03 Rewritten to only work with Timer1 and support direct hardware output 0008 B Cook 10/05/03 Rewritten so the timer can be selected at compile time 0009 T Carpenter 12/08/06 Rewritten to remove requirement for all the wierd timer name creation macros. *************************************************/ #include #include "Arduino.h" #include "wiring_private.h" #include "pins_arduino.h" // timerx_toggle_count: // > 0 - duration specified // = 0 - stopped // < 0 - infinitely (until stop() method called, or new play() called) static volatile long tone_timer_toggle_count; static volatile uint8_t *tone_timer_pin_register; static volatile uint8_t tone_timer_pin_mask; static uint8_t tone_pin = 255; void tone( uint8_t _pin, unsigned int frequency, unsigned long duration ) { if ( tone_pin == 255 ) { /* Set the timer to power-up conditions so we start from a known state */ // Ensure the timer is in the same state as power-up #if (TIMER_TO_USE_FOR_TONE == 0) TCCR0B = (0< 0 ) { /* Determine which prescaler to use */ /* Set the Output Compare Register (rounding up) */ #if TIMER_TO_USE_FOR_TONE == 1 uint16_t ocr = F_CPU / frequency / 2; #if defined(TCCR1E) uint8_t prescalarbits = 0b0001; if (ocr > 256) { ocr >>= 3; //divide by 8 prescalarbits = 0b0100; // ck/8 if (ocr > 256) { ocr >>= 3; //divide by a further 8 prescalarbits = 0b0111; //ck/64 if (ocr > 256) { ocr >>= 2; //divide by a further 4 prescalarbits = 0b1001; //ck/256 if (ocr > 256) { // can't do any better than /1024 ocr >>= 2; //divide by a further 4 prescalarbits = 0b1011; //ck/1024 } } } } #else #if defined(TCCR1) uint8_t prescalarbits = 0b0001; #else uint8_t prescalarbits = 0b001; #endif if (ocr > 0xffff) { ocr /= 64; #if defined(TCCR1) prescalarbits = 0b0111; #else prescalarbits = 0b011; #endif } #endif ocr -= 1; //Note we are doing the subtraction of 1 here to save repeatedly calculating ocr from just the frequency in the if tree above OCR1A = ocr; #elif TIMER_TO_USE_FOR_TONE == 0 uint16_t ocr = F_CPU / frequency / 2; uint8_t prescalarbits = 0b001; // ck/1 if (ocr > 256) { ocr >>= 3; //divide by 8 prescalarbits = 0b010; // ck/8 if (ocr > 256) { ocr >>= 3; //divide by a further 8 prescalarbits = 0b011; //ck/64 if (ocr > 256) { ocr >>= 2; //divide by a further 4 prescalarbits = 0b100; //ck/256 if (ocr > 256) { // can't do any better than /1024 ocr >>= 2; //divide by a further 4 prescalarbits = 0b101; //ck/1024 } } } } ocr -= 1; //Note we are doing the subtraction of 1 here to save repeatedly calculating ocr from just the frequency in the if tree above OCR0A = ocr; #endif /* Does the caller want a specific duration? */ if ( duration > 0 ) { /* Determine how many times the value toggles */ tone_timer_toggle_count = (2 * frequency * duration) / 1000; /* Output Compare A Match Interrupt Enable */ #if (TIMER_TO_USE_FOR_TONE == 1) #if defined (TIMSK) TIMSK |= (1< 0 ) { --tone_timer_toggle_count; if ( tone_timer_toggle_count == 0 ) { // Shutdown the hardware noTone( 255 ); // Skip the rest. We're finished. return; } } *tone_timer_pin_register ^= tone_timer_pin_mask; } else { // Shutdown the hardware noTone( 255 ); } }