mirror of
https://github.com/digistump/DigistumpArduino.git
synced 2025-04-27 15:19:02 -07:00
505 lines
15 KiB
C++
505 lines
15 KiB
C++
/* 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
|
|
|
|
*************************************************/
|
|
|
|
#define DEBUG_TONE 0
|
|
|
|
#include <avr/interrupt.h>
|
|
#include "core_build_options.h"
|
|
#include "ToneTimer.h"
|
|
#include "pins_arduino.h"
|
|
#include "wiring.h"
|
|
|
|
#if (TONETIMER_NUMBER_PRESCALERS != 5) && (TONETIMER_NUMBER_PRESCALERS != 15)
|
|
#error Only five or fifteen prescalers are supported. Update the code to support the number of actual prescalers.
|
|
#endif
|
|
|
|
#if TONETIMER_NUMBER_PRESCALERS == 15
|
|
#define TONETIMER_MAXIMUM_DIVISOR ( (unsigned long)(TONETIMER_(PRESCALER_VALUE_15)) * (1L + (unsigned long)(TONETIMER_(MAXIMUM_OCR))) )
|
|
#endif
|
|
|
|
#if TONETIMER_NUMBER_PRESCALERS == 5
|
|
#define TONETIMER_MAXIMUM_DIVISOR ( (unsigned long)(TONETIMER_(PRESCALER_VALUE_5)) * (1L + (unsigned long)(TONETIMER_(MAXIMUM_OCR))) )
|
|
#endif
|
|
|
|
const unsigned int Tone_Lowest_Frequency = (F_CPU + (2L * TONETIMER_MAXIMUM_DIVISOR - 1L)) / (2L * TONETIMER_MAXIMUM_DIVISOR);
|
|
|
|
#if (TONETIMER_(MAXIMUM_OCR) == 65535) && (TONETIMER_(PRESCALE_SET) == 1)
|
|
#if F_CPU <= 1000000
|
|
#define TONE_FREQUENCY_CUTOFF_2 (7)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#elif F_CPU <= 8000000
|
|
#define TONE_FREQUENCY_CUTOFF_3 (7)
|
|
#define TONE_FREQUENCY_CUTOFF_2 (61)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#elif F_CPU <= 16000000
|
|
#define TONE_FREQUENCY_CUTOFF_4 (1)
|
|
#define TONE_FREQUENCY_CUTOFF_3 (15)
|
|
#define TONE_FREQUENCY_CUTOFF_2 (122)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#elif F_CPU <= 16500000
|
|
#define TONE_FREQUENCY_CUTOFF_4 (1)
|
|
#define TONE_FREQUENCY_CUTOFF_3 (15)
|
|
#define TONE_FREQUENCY_CUTOFF_2 (122)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#endif
|
|
#endif
|
|
|
|
#if (TONETIMER_(MAXIMUM_OCR) == 255) && (TONETIMER_(PRESCALE_SET) == 1)
|
|
#if F_CPU <= 1000000
|
|
#define TONE_FREQUENCY_CUTOFF_5 (7)
|
|
#define TONE_FREQUENCY_CUTOFF_4 (30)
|
|
#define TONE_FREQUENCY_CUTOFF_3 (243)
|
|
#define TONE_FREQUENCY_CUTOFF_2 (1949)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#elif F_CPU <= 8000000
|
|
#define TONE_FREQUENCY_CUTOFF_5 (60)
|
|
#define TONE_FREQUENCY_CUTOFF_4 (243)
|
|
#define TONE_FREQUENCY_CUTOFF_3 (1949)
|
|
#define TONE_FREQUENCY_CUTOFF_2 (15594)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#elif F_CPU <= 16000000
|
|
#define TONE_FREQUENCY_CUTOFF_5 (121)
|
|
#define TONE_FREQUENCY_CUTOFF_4 (487)
|
|
#define TONE_FREQUENCY_CUTOFF_3 (3898)
|
|
#define TONE_FREQUENCY_CUTOFF_2 (31189)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#elif F_CPU <= 16500000
|
|
#define TONE_FREQUENCY_CUTOFF_5 (121)
|
|
#define TONE_FREQUENCY_CUTOFF_4 (487)
|
|
#define TONE_FREQUENCY_CUTOFF_3 (3898)
|
|
#define TONE_FREQUENCY_CUTOFF_2 (31189)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#endif
|
|
#endif
|
|
|
|
#if (TONETIMER_(MAXIMUM_OCR) == 255) && (TONETIMER_(PRESCALE_SET) == 2)
|
|
#if F_CPU <= 1000000
|
|
#define TONE_FREQUENCY_CUTOFF_12 (1)
|
|
#define TONE_FREQUENCY_CUTOFF_11 (3)
|
|
#define TONE_FREQUENCY_CUTOFF_10 (7)
|
|
#define TONE_FREQUENCY_CUTOFF_9 (15)
|
|
#define TONE_FREQUENCY_CUTOFF_8 (30)
|
|
#define TONE_FREQUENCY_CUTOFF_7 (60)
|
|
#define TONE_FREQUENCY_CUTOFF_6 (121)
|
|
#define TONE_FREQUENCY_CUTOFF_5 (243)
|
|
#define TONE_FREQUENCY_CUTOFF_4 (487)
|
|
#define TONE_FREQUENCY_CUTOFF_3 (974)
|
|
#define TONE_FREQUENCY_CUTOFF_2 (1949)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#elif F_CPU <= 8000000
|
|
#define TONE_FREQUENCY_CUTOFF_15 (1)
|
|
#define TONE_FREQUENCY_CUTOFF_14 (3)
|
|
#define TONE_FREQUENCY_CUTOFF_13 (7)
|
|
#define TONE_FREQUENCY_CUTOFF_12 (15)
|
|
#define TONE_FREQUENCY_CUTOFF_11 (30)
|
|
#define TONE_FREQUENCY_CUTOFF_10 (60)
|
|
#define TONE_FREQUENCY_CUTOFF_9 (121)
|
|
#define TONE_FREQUENCY_CUTOFF_8 (243)
|
|
#define TONE_FREQUENCY_CUTOFF_7 (487)
|
|
#define TONE_FREQUENCY_CUTOFF_6 (974)
|
|
#define TONE_FREQUENCY_CUTOFF_5 (1949)
|
|
#define TONE_FREQUENCY_CUTOFF_4 (3898)
|
|
#define TONE_FREQUENCY_CUTOFF_3 (7797)
|
|
#define TONE_FREQUENCY_CUTOFF_2 (15594)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#elif F_CPU <= 16000000
|
|
#define TONE_FREQUENCY_CUTOFF_15 (3)
|
|
#define TONE_FREQUENCY_CUTOFF_14 (7)
|
|
#define TONE_FREQUENCY_CUTOFF_13 (15)
|
|
#define TONE_FREQUENCY_CUTOFF_12 (30)
|
|
#define TONE_FREQUENCY_CUTOFF_11 (60)
|
|
#define TONE_FREQUENCY_CUTOFF_10 (121)
|
|
#define TONE_FREQUENCY_CUTOFF_9 (243)
|
|
#define TONE_FREQUENCY_CUTOFF_8 (487)
|
|
#define TONE_FREQUENCY_CUTOFF_7 (974)
|
|
#define TONE_FREQUENCY_CUTOFF_6 (1949)
|
|
#define TONE_FREQUENCY_CUTOFF_5 (3898)
|
|
#define TONE_FREQUENCY_CUTOFF_4 (7797)
|
|
#define TONE_FREQUENCY_CUTOFF_3 (15594)
|
|
#define TONE_FREQUENCY_CUTOFF_2 (31189)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#elif F_CPU <= 16500000
|
|
#define TONE_FREQUENCY_CUTOFF_15 (3)
|
|
#define TONE_FREQUENCY_CUTOFF_14 (7)
|
|
#define TONE_FREQUENCY_CUTOFF_13 (15)
|
|
#define TONE_FREQUENCY_CUTOFF_12 (30)
|
|
#define TONE_FREQUENCY_CUTOFF_11 (60)
|
|
#define TONE_FREQUENCY_CUTOFF_10 (121)
|
|
#define TONE_FREQUENCY_CUTOFF_9 (243)
|
|
#define TONE_FREQUENCY_CUTOFF_8 (487)
|
|
#define TONE_FREQUENCY_CUTOFF_7 (974)
|
|
#define TONE_FREQUENCY_CUTOFF_6 (1949)
|
|
#define TONE_FREQUENCY_CUTOFF_5 (3898)
|
|
#define TONE_FREQUENCY_CUTOFF_4 (7797)
|
|
#define TONE_FREQUENCY_CUTOFF_3 (15594)
|
|
#define TONE_FREQUENCY_CUTOFF_2 (31189)
|
|
#define TONE_FREQUENCY_CUTOFF_1 (65535)
|
|
#endif
|
|
#endif
|
|
|
|
|
|
#if DEBUG_TONE
|
|
uint16_t debug_tone_last_OCRxA;
|
|
uint16_t debug_tone_last_CSV;
|
|
#endif
|
|
|
|
|
|
// 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 )
|
|
{
|
|
tonetimer_(ocr_t) ocr;
|
|
tonetimer_(prescale_value_t) csv;
|
|
tonetimer_(cs_t) csi;
|
|
|
|
if ( tone_pin == 255 )
|
|
{
|
|
/* Set the timer to power-up conditions so we start from a known state */
|
|
ToneTimer_SetToPowerup();
|
|
|
|
/*
|
|
Compare Output Mode = Normal port operation, OCxA/OCxB disconnected.
|
|
Waveform Generation Mode = 4; 0100; CTC; (Clear Timer on Compare); OCR1A; Immediate; MAX
|
|
Clock Select = No clock source (Timer/Counter stopped).
|
|
Note: Turn off the clock first to avoid ticks and scratches.
|
|
*/
|
|
ToneTimer_SetWaveformGenerationMode( ToneTimer_(CTC_OCR) );
|
|
|
|
/* If the tone pin can be driven directly from the timer */
|
|
|
|
if ( (_pin == ToneTimer_OutputComparePinA) || (_pin == ToneTimer_OutputComparePinB) )
|
|
{
|
|
/* Pin toggling is handled by the hardware */
|
|
tone_timer_pin_register = NULL;
|
|
tone_timer_pin_mask = 0;
|
|
|
|
if ( _pin == ToneTimer_OutputComparePinA )
|
|
{
|
|
/* Compare Output Mode = Toggle OCxA on Compare Match. */
|
|
ToneTimer_SetCompareOutputModeA( ToneTimer_(Toggle) );
|
|
}
|
|
else // if ( _pin == ToneTimer_OutputComparePinB )
|
|
{
|
|
/* Compare Output Mode = Toggle OCxA on Compare Match. */
|
|
ToneTimer_SetCompareOutputModeB( ToneTimer_(Toggle) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Save information needed by the interrupt service routine */
|
|
tone_timer_pin_register = portOutputRegister( digitalPinToPort( _pin ) );
|
|
tone_timer_pin_mask = digitalPinToBitMask( _pin );
|
|
|
|
/* Compare Output Mode = Normal port operation, OCxA disconnected. */
|
|
ToneTimer_DisconnectOutputs();
|
|
}
|
|
|
|
/* Ensure the pin is configured for output */
|
|
pinMode( _pin, OUTPUT );
|
|
|
|
tone_pin = _pin;
|
|
}
|
|
|
|
if ( tone_pin == _pin )
|
|
{
|
|
/* Stop the clock while we make changes. */
|
|
|
|
ToneTimer_ClockSelect( ToneTimer_(Stopped) );
|
|
|
|
/* Start the counter at zero to reduce ticks and scratches. */
|
|
|
|
ToneTimer_SetCount( 0 );
|
|
|
|
if ( frequency > 0 )
|
|
{
|
|
if ( frequency < Tone_Lowest_Frequency )
|
|
{
|
|
frequency = Tone_Lowest_Frequency;
|
|
}
|
|
|
|
/* Determine which prescaler to use */
|
|
/* Set the Output Compare Register (rounding up) */
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_15 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_15 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_15);
|
|
csi = ToneTimer_(Prescale_Index_15);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_14 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_14 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_14);
|
|
csi = ToneTimer_(Prescale_Index_14);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_13 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_13 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_13);
|
|
csi = ToneTimer_(Prescale_Index_13);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_12 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_12 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_12);
|
|
csi = ToneTimer_(Prescale_Index_12);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_11 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_11 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_11);
|
|
csi = ToneTimer_(Prescale_Index_11);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_10 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_10 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_10);
|
|
csi = ToneTimer_(Prescale_Index_10);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_9 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_9 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_9);
|
|
csi = ToneTimer_(Prescale_Index_9);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_8 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_8 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_8);
|
|
csi = ToneTimer_(Prescale_Index_8);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_7 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_7 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_7);
|
|
csi = ToneTimer_(Prescale_Index_7);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_6 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_6 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_6);
|
|
csi = ToneTimer_(Prescale_Index_6);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_5 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_5 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_5);
|
|
csi = ToneTimer_(Prescale_Index_5);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_4 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_4 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_4);
|
|
csi = ToneTimer_(Prescale_Index_4);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
#if defined( TONE_FREQUENCY_CUTOFF_3 )
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_3 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_3);
|
|
csi = ToneTimer_(Prescale_Index_3);
|
|
}
|
|
else
|
|
#endif
|
|
|
|
if ( frequency <= TONE_FREQUENCY_CUTOFF_2 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_2);
|
|
csi = ToneTimer_(Prescale_Index_2);
|
|
}
|
|
|
|
else // if ( frequency <= TONE_FREQUENCY_CUTOFF_1 )
|
|
{
|
|
csv = TONETIMER_(PRESCALER_VALUE_1);
|
|
csi = ToneTimer_(Prescale_Index_1);
|
|
}
|
|
|
|
ocr = ( (2L * F_CPU) / (frequency * 2L * csv) + 1L ) / 2L - 1L;
|
|
ToneTimer_SetOutputCompareMatchAndClear( ocr );
|
|
|
|
#if DEBUG_TONE
|
|
debug_tone_last_OCRxA = ocr;
|
|
debug_tone_last_CSV = csv;
|
|
#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 */
|
|
ToneTimer_EnableOutputCompareInterruptA();
|
|
}
|
|
else
|
|
{
|
|
/* Indicate to the interrupt service routine that we'll be running until further notice */
|
|
tone_timer_toggle_count = -1;
|
|
|
|
/* All pins but the OCxA / OCxB pins have to be driven by software */
|
|
if ( (_pin != ToneTimer_OutputComparePinA) && (_pin != ToneTimer_OutputComparePinB) )
|
|
{
|
|
/* Output Compare A Match Interrupt Enable */
|
|
ToneTimer_EnableOutputCompareInterruptA();
|
|
}
|
|
}
|
|
|
|
/* Start the clock... */
|
|
|
|
ToneTimer_ClockSelect( csi );
|
|
}
|
|
else
|
|
{
|
|
/* To be on the safe side, turn off all interrupts */
|
|
ToneTimer_InterruptsOff();
|
|
|
|
/* Clock is stopped. Counter is zero. The only thing left to do is turn off the output. */
|
|
digitalWrite( _pin, 0 );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void noTone( uint8_t _pin )
|
|
{
|
|
if ( (tone_pin != 255)
|
|
&& ((tone_pin == _pin) || (_pin == 255)) )
|
|
{
|
|
// Turn off all interrupts
|
|
ToneTimer_InterruptsOff();
|
|
|
|
// Stop the clock while we make changes.
|
|
ToneTimer_ClockSelect( ToneTimer_(Stopped) );
|
|
|
|
// Set the Tone Timer exactly the same as init did...
|
|
initToneTimer();
|
|
|
|
//rmv ToneTimer_SetToPowerup();
|
|
|
|
/* rmv
|
|
// put timer 1 in 8-bit phase correct pwm mode
|
|
TCCR1A = (0<<COM1A1)|(0<<COM1A0) | (0<<COM1B1)|(0<<COM1B0) | (0<<WGM11)|(1<<WGM10);
|
|
|
|
// set timer 1 prescale factor to 64
|
|
// and start the timer
|
|
TCCR1B = (0<<ICNC1) | (0<<ICES1) | (0<<WGM13)|(0<<WGM12) | (0<<CS12)|(1<<CS11)|(1<<CS10);
|
|
*/
|
|
|
|
// Set the output low
|
|
if ( tone_timer_pin_register != NULL )
|
|
{
|
|
*tone_timer_pin_register &= ~(tone_timer_pin_mask);
|
|
}
|
|
else
|
|
{
|
|
digitalWrite( tone_pin, LOW );
|
|
}
|
|
|
|
tone_pin = 255;
|
|
}
|
|
}
|
|
|
|
|
|
ISR( TONETIMER_COMPA_vect, ISR_NOBLOCK )
|
|
{
|
|
if ( tone_timer_toggle_count != 0 )
|
|
{
|
|
if ( tone_timer_toggle_count > 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 );
|
|
}
|
|
}
|