961 lines
24 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 M Sproul 10/08/29 Changed #ifdefs from cpu to register
0008 S Kanemoto 12/06/22 Fixed for Leonardo by @maris_HY
*************************************************/
// COMPLETE re-write for ATXMega by Bob Frazier, S.F.T. Inc. - http://mrp3.com/
// NOTE: this still only supports one tone output. However, xmega can do more than one
// due to the way the timers are. In fact, 'E' series can probably do a LOT more
// than one. If you want to implement that, it's a public project, so get it working
// reliably and submit the changes, thanks.
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "Arduino.h"
#include "pins_arduino.h"
#if defined(TCC4) || !defined(TCC2)
// in these cases this file isn't ready for prime time, so disable it for now
#ifdef TONE_SUPPORTED
#undef TONE_SUPPORTED
#endif // TONE_SUPPORTED
#else // !TCC4 && TCC2
#ifndef TONE_SUPPORTED
#define TONE_SUPPORTED // for now turn it off for 'E"
#endif // TONE_SUPPORTED
#endif // TCC4 || !TCC2
#ifdef TONE_SUPPORTED
#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
#define PROGMEM_ORIG PROGMEM
#else // PROGMEM workaround
// to avoid the bogus "initialized variables" warning
#ifdef PROGMEM
#undef PROGMEM
#endif // PROGMEM re-define
#define PROGMEM __attribute__((section(".progmem.tone")))
#define PROGMEM_ORIG __attribute__((__progmem__))
#endif // check for GNUC >= or < 4.6
static PORT_t *pTonePort = NULL; // must assign at startup due to ISR
static uint8_t bToneMask = 0; // bitmask for tone pin
static unsigned long toggle_count = 0; // number of cycles to output
static void toneBegin(uint8_t _pin, uint8_t _div, uint16_t _per)
{
pTonePort = (PORT_t *)portModeRegister(digitalPinToPort(_pin));
bToneMask = digitalPinToBitMask(_pin);
// Set the pinMode as OUTPUT
pinMode(_pin, OUTPUT);
#if NUM_DIGITAL_PINS > 18 /* meaning there is a PORT E available */
TCE0_INTCTRLA = 0; // temporarily disable overflow interrupt
TCE0_INTCTRLB = 0; // disable other interrupts
TCE0_CTRLA = _div; // divisor for pre-scaler
TCE0_CTRLB = TC_WGMODE_NORMAL_gc; // 'normal' mode (interrupt on 'overflow')
TCE0_CTRLD = 0; // not an event timer, 16-bit mode (12.11.4)
TCE0_CTRLE = 0; // 16-bit mode
TCE0_PER = _per; // period (16-bit value)
TCE0_INTCTRLA = 3; // overflow int level 3 (enables interrupt)
#elif defined(TCC4) // E series and anything else with 'TCC4'
TCC4_INTCTRLA = 0; // temporarily disable overflow interrupt
TCC4_INTCTRLB = 0; // disable other interrupts
TCC4_CTRLA = _div; // divisor for pre-scaler
TCC4_CTRLB = TC45_WGMODE_NORMAL_gc; // 'normal' mode (interrupt on 'overflow')
TCC4_CTRLD = 0; // not an event timer, 16-bit mode (12.11.4)
TCC4_CTRLE = 0; // 16-bit mode
TCC4_PER = _per; // period (16-bit value)
TCC4_INTCTRLA = 3; // overflow int level 3 (enables interrupt)
#else // other stuff not yet explored by me
TCC0_INTCTRLA = 0; // temporarily disable overflow interrupt
TCC0_INTCTRLB = 0; // disable other interrupts
TCC0_CTRLA = _div; // divisor for pre-scaler
TCC0_CTRLB = TC_WGMODE_NORMAL_gc; // 'normal' mode (interrupt on 'overflow')
TCC0_CTRLD = 0; // not an event timer, 16-bit mode (12.11.4)
TCC0_CTRLE = 0; // 16-bit mode
TCC0_PER = _per; // period (16-bit value)
TCC0_INTCTRLA = 3; // overflow int level 3 (enables interrupt)
#endif // NUM_DIGITAL_PINS > 18
// tone starts now, shuts off when the 'toggle_count' hits zero
}
// frequency (in hertz) and duration (in milliseconds).
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration)
{
register int8_t b1;
unsigned short per, w2;
unsigned long ulTemp;
static const uint16_t aPreScaler[] PROGMEM = {1,2,4,8,64,256,1024}; // pre-scaler
// frequency
// based on the frequency, set up the divider and period
// period is 16-bits
// NOTE: use the smallest possible divisor
if(!frequency)
{
return;
}
ulTemp = frequency * 16384L; // ideal counter 16384
for(b1=sizeof(aPreScaler)/sizeof(aPreScaler[0]) - 1; b1 > 0; b1--)
{
w2 = pgm_read_word(&(aPreScaler[0]) + b1);
if(((unsigned long)F_CPU / 2 / w2) >= ulTemp) // note that I flip the bit every OTHER cycle
{
break;
}
}
if(!b1)
{
w2 = 1; // make sure
}
// b1 is the divisor bit value for CTRLA, per caches the actual divisor
per = (F_CPU / 2 / w2) / frequency;
if(!per)
{
per++;
}
// Calculate the toggle count
if (duration > 0)
{
toggle_count = 2 * frequency * duration / 1000;
}
else
{
toggle_count = -1;
}
toneBegin(_pin, b1, per);
}
// XXX: this function only works properly for timer E (the only one we use
// currently). Since I use the ISR on timer E to toggle the pin, it should
// be just fine.
void disableTimer(uint8_t _timer)
{
// parameter is ignored
#if NUM_DIGITAL_PINS > 18 /* meaning there is a PORT E available */
// disable under/overflow and comparison interrupts FIRST
TCE0_INTCTRLA = 0; // no underflow interrupts
TCE0_INTCTRLB = 0; // no comparison interrupts
pTonePort = NULL; // make sure
// re-assign TCE0 defaults. see 'wiring.c'
#if NUM_DIGITAL_PINS > 22 /* meaning PORTE has 8 pins */
TCE2_CTRLA = 5; // b0101 - divide by 64 - D manual 13.9.1
TCE2_CTRLB = 0; // compare outputs disabled on all 8 bits (13.9.2)
// TCE2_CTRLC = 0; // when timer not running, sets compare (13.9.3)
TCE2_CTRLE = 0x2; // b10 - 'split' mode - D manual 13.9.4
TCE2_CTRLF = 0; // not resetting or anything (13.9.7)
TCE2_LPER = 255; // count 255 to 0 (total period = 256)
TCE2_HPER = 255;
// pre-assign comparison registers to 'zero' (for PWM out) which is actually 255
// 'timer 2' counts DOWN. This, however, would generate a '1' output.
TCE2_LCMPA = 255;
TCE2_LCMPB = 255;
TCE2_LCMPC = 255;
TCE2_LCMPD = 255;
TCE2_HCMPA = 255;
TCE2_HCMPB = 255;
TCE2_HCMPC = 255;
TCE2_HCMPD = 255;
TCE2_INTCTRLA = 0; // no underflow interrupts
TCE2_INTCTRLB = 0; // no comparison interrupts
#else // 16-bit timer on TCE0
TCE0_CTRLA = 5; // b0101 - divide by 64 - D manual 12.11.1
TCE0_CTRLB = TC_WGMODE_SS_gc; // single-slope PWM. NOTE: this counts UP, whereas the other timers count DOWN
// other bits (high nybble) are OFF - they enable output on the 4 port E pins
// TCE0_CTRLC = 0; // when timer not running, sets compare (12.11.3)
TCE0_CTRLD = 0; // not an event timer, 16-bit mode (12.11.4)
TCE0_CTRLE = 1; // normal 8-bit timer (set to 0 for 16-bit mode) (12.11.5)
// make sure the timer E 'period' register is correctly set at 255 (i.e. 0-255 or 256 clock cycles).
TCE0_PER = 255;
// pre-assign comparison registers to 'zero' (for PWM out) which is actually 255
// timer 0 can be configured to count UP or DOWN, but for single-slope PWM it is
// always 'UP'. A value of '255' should generate a '1' output for each PWM.
TCE0_CCA = 255;
TCE0_CCB = 255;
TCE0_CCC = 255;
TCE0_CCD = 255;
#endif // 8/16 bit timer on E
#elif defined(TCC4) // E series and anything else with 'TCC4'
// disable under/overflow and comparison interrupts FIRST
TCC4_INTCTRLA = 0; // no underflow interrupts
TCC4_INTCTRLB = 0; // no comparison interrupts
pTonePort = NULL; // make sure
// re-assign TCC0 defaults. see 'wiring.c'
TCC4_CTRLA = 5; // b0101 - divide by 64 - E manual 13.13.1
TCC4_CTRLB = TC45_BYTEM_BYTEMODE_gc | TC45_WGMODE_SINGLESLOPE_gc; // byte mode, single slope
// TCC5_CTRLC = 0; // when timer not running, sets compare (13.9.3)
TCC4_CTRLD = 0; // events off
TCC4_CTRLE = 0; // no output on L pins
TCC4_CTRLF = 0; // no output on H pins
TCC4_PER = 255; // 255 for period limit
// pre-assign comparison registers to 'zero' (for PWM out) which is actually 255
// 'timer 2' counts DOWN.
TCC4_CCA = 65535;
TCC4_CCB = 65535;
TCC4_CCC = 65535;
TCC4_CCD = 65535;
#else // other stuff not yet explored by me
// disable under/overflow and comparison interrupts FIRST
TCC0_INTCTRLA = 0; // no underflow interrupts
TCC0_INTCTRLB = 0; // no comparison interrupts
pTonePort = NULL; // make sure
// re-assign TCC0 defaults. see 'wiring.c'
TCC2_CTRLA = 5; // b0101 - divide by 64 - D manual 13.9.1
TCC2_CTRLB = 0; // compare outputs disabled on all 8 bits (13.9.2)
// TCC2_CTRLC = 0; // when timer not running, sets compare (13.9.3)
TCC2_CTRLE = 0x2; // b10 - 'split' mode - D manual 13.9.4
TCC2_CTRLF = 0; // not resetting or anything (13.9.7)
TCC2_LPER = 255; // count 255 to 0 (total period = 256)
TCC2_HPER = 255;
// pre-assign comparison registers to 'zero' (for PWM out) which is actually 255
// 'timer 2' counts DOWN. This, however, would generate a '1' output.
TCC2_LCMPA = 255;
TCC2_LCMPB = 255;
TCC2_LCMPC = 255;
TCC2_LCMPD = 255;
TCC2_HCMPA = 255;
TCC2_HCMPB = 255;
TCC2_HCMPC = 255;
TCC2_HCMPD = 255;
TCC2_INTCTRLA = 0; // no underflow interrupts
TCC2_INTCTRLB = 0; // no comparison interrupts
#endif // NUM_DIGITAL_PINS > 18
}
void noTone(uint8_t _pin)
{
disableTimer(0);
digitalWrite(_pin, 0);
}
#if NUM_DIGITAL_PINS > 18 /* meaning PORTE exists */
ISR(TCE0_OVF_vect) // the 'overflow' vector on timer E0
#elif defined(TCC4) // E series and anything else with 'TCC4'
ISR(TCC4_OVF_vect) // the 'overflow' vector on timer C4
#else // everything else
ISR(TCC0_OVF_vect) // the 'overflow' vector on timer C0
#endif // PORTE exist check
{
if(!toggle_count || !pTonePort || !bToneMask
#if 1 /* this section in for bullet-proofing, consider removing */
|| (pTonePort != &PORTA &&
#if NUM_ANALOG_PINS > 8
pTonePort != &PORTB &&
#endif // NUM_ANALOG_PINS > 8
pTonePort != &PORTC && pTonePort != &PORTD &&
#if NUM_DIGITAL_PINS > 18
pTonePort != &PORTE &&
#endif // PORTE exist check
pTonePort != &PORTR)
#endif // 1
)
{
// disable the timer (also disables the interrupt)
disableTimer(0);
return;
}
// each time I get an overflow, toggle the tone pin
pTonePort->OUTTGL = bToneMask; // toggle that bit
toggle_count--;
}
#if 0 // OLD CODE for reference only
#if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__)
#define TCCR2A TCCR2
#define TCCR2B TCCR2
#define COM2A1 COM21
#define COM2A0 COM20
#define OCR2A OCR2
#define TIMSK2 TIMSK
#define OCIE2A OCIE2
#define TIMER2_COMPA_vect TIMER2_COMP_vect
#define TIMSK1 TIMSK
#endif
// timerx_toggle_count:
// > 0 - duration specified
// = 0 - stopped
// < 0 - infinitely (until stop() method called, or new play() called)
#if !defined(__AVR_ATmega8__)
volatile long timer0_toggle_count;
volatile uint8_t *timer0_pin_port;
volatile uint8_t timer0_pin_mask;
#endif
volatile long timer1_toggle_count;
volatile uint8_t *timer1_pin_port;
volatile uint8_t timer1_pin_mask;
volatile long timer2_toggle_count;
volatile uint8_t *timer2_pin_port;
volatile uint8_t timer2_pin_mask;
#if defined(TIMSK3)
volatile long timer3_toggle_count;
volatile uint8_t *timer3_pin_port;
volatile uint8_t timer3_pin_mask;
#endif
#if defined(TIMSK4)
volatile long timer4_toggle_count;
volatile uint8_t *timer4_pin_port;
volatile uint8_t timer4_pin_mask;
#endif
#if defined(TIMSK5)
volatile long timer5_toggle_count;
volatile uint8_t *timer5_pin_port;
volatile uint8_t timer5_pin_mask;
#endif
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define AVAILABLE_TONE_PINS 1
#define USE_TIMER2
const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2 /*, 3, 4, 5, 1, 0 */ };
static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255 /*, 255, 255, 255, 255, 255 */ };
#elif defined(__AVR_ATmega8__)
#define AVAILABLE_TONE_PINS 1
#define USE_TIMER2
const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2 /*, 1 */ };
static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255 /*, 255 */ };
#elif defined(__AVR_ATmega32U4__)
#define AVAILABLE_TONE_PINS 1
#define USE_TIMER3
const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 3 /*, 1 */ };
static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255 /*, 255 */ };
#else
#define AVAILABLE_TONE_PINS 1
#define USE_TIMER2
// Leave timer 0 to last.
const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2 /*, 1, 0 */ };
static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255 /*, 255, 255 */ };
#endif
// NOTE: K&R coding style edited away. Allman style rules - BF
static int8_t toneBegin(uint8_t _pin)
{
int8_t _timer = -1;
// if we're already using the pin, the timer should be configured.
for (int i = 0; i < AVAILABLE_TONE_PINS; i++)
{
if (tone_pins[i] == _pin)
{
return pgm_read_byte(tone_pin_to_timer_PGM + i);
}
}
// search for an unused timer.
for (int i = 0; i < AVAILABLE_TONE_PINS; i++)
{
if (tone_pins[i] == 255)
{
tone_pins[i] = _pin;
_timer = pgm_read_byte(tone_pin_to_timer_PGM + i);
break;
}
}
if (_timer != -1)
{
// Set timer specific stuff
// All timers in CTC mode
// 8 bit timers will require changing prescalar values,
// whereas 16 bit timers are set to either ck/1 or ck/64 prescalar
switch (_timer)
{
#if defined(TCCR0A) && defined(TCCR0B)
case 0:
// 8 bit timer
TCCR0A = 0;
TCCR0B = 0;
bitWrite(TCCR0A, WGM01, 1);
bitWrite(TCCR0B, CS00, 1);
timer0_pin_port = portOutputRegister(digitalPinToPort(_pin));
timer0_pin_mask = digitalPinToBitMask(_pin);
break;
#endif
#if defined(TCCR1A) && defined(TCCR1B) && defined(WGM12)
case 1:
// 16 bit timer
TCCR1A = 0;
TCCR1B = 0;
bitWrite(TCCR1B, WGM12, 1);
bitWrite(TCCR1B, CS10, 1);
timer1_pin_port = portOutputRegister(digitalPinToPort(_pin));
timer1_pin_mask = digitalPinToBitMask(_pin);
break;
#endif
#if defined(TCCR2A) && defined(TCCR2B)
case 2:
// 8 bit timer
TCCR2A = 0;
TCCR2B = 0;
bitWrite(TCCR2A, WGM21, 1);
bitWrite(TCCR2B, CS20, 1);
timer2_pin_port = portOutputRegister(digitalPinToPort(_pin));
timer2_pin_mask = digitalPinToBitMask(_pin);
break;
#endif
#if defined(TCCR3A) && defined(TCCR3B) && defined(TIMSK3)
case 3:
// 16 bit timer
TCCR3A = 0;
TCCR3B = 0;
bitWrite(TCCR3B, WGM32, 1);
bitWrite(TCCR3B, CS30, 1);
timer3_pin_port = portOutputRegister(digitalPinToPort(_pin));
timer3_pin_mask = digitalPinToBitMask(_pin);
break;
#endif
#if defined(TCCR4A) && defined(TCCR4B) && defined(TIMSK4)
case 4:
// 16 bit timer
TCCR4A = 0;
TCCR4B = 0;
#if defined(WGM42)
bitWrite(TCCR4B, WGM42, 1);
#elif defined(CS43)
#warning this may not be correct
// atmega32u4
bitWrite(TCCR4B, CS43, 1);
#endif
bitWrite(TCCR4B, CS40, 1);
timer4_pin_port = portOutputRegister(digitalPinToPort(_pin));
timer4_pin_mask = digitalPinToBitMask(_pin);
break;
#endif
#if defined(TCCR5A) && defined(TCCR5B) && defined(TIMSK5)
case 5:
// 16 bit timer
TCCR5A = 0;
TCCR5B = 0;
bitWrite(TCCR5B, WGM52, 1);
bitWrite(TCCR5B, CS50, 1);
timer5_pin_port = portOutputRegister(digitalPinToPort(_pin));
timer5_pin_mask = digitalPinToBitMask(_pin);
break;
#endif
}
}
return _timer;
}
// frequency (in hertz) and duration (in milliseconds).
void tone(uint8_t _pin, unsigned int frequency, unsigned long duration)
{
uint8_t prescalarbits = 0b001;
long toggle_count = 0;
uint32_t ocr = 0;
int8_t _timer;
_timer = toneBegin(_pin);
if (_timer >= 0)
{
// Set the pinMode as OUTPUT
pinMode(_pin, OUTPUT);
// if we are using an 8 bit timer, scan through prescalars to find the best fit
if (_timer == 0 || _timer == 2)
{
ocr = F_CPU / frequency / 2 - 1;
prescalarbits = 0b001; // ck/1: same for both timers
if (ocr > 255)
{
ocr = F_CPU / frequency / 2 / 8 - 1;
prescalarbits = 0b010; // ck/8: same for both timers
if (_timer == 2 && ocr > 255)
{
ocr = F_CPU / frequency / 2 / 32 - 1;
prescalarbits = 0b011;
}
if (ocr > 255)
{
ocr = F_CPU / frequency / 2 / 64 - 1;
prescalarbits = _timer == 0 ? 0b011 : 0b100;
if (_timer == 2 && ocr > 255)
{
ocr = F_CPU / frequency / 2 / 128 - 1;
prescalarbits = 0b101;
}
if (ocr > 255)
{
ocr = F_CPU / frequency / 2 / 256 - 1;
prescalarbits = _timer == 0 ? 0b100 : 0b110;
if (ocr > 255)
{
// can't do any better than /1024
ocr = F_CPU / frequency / 2 / 1024 - 1;
prescalarbits = _timer == 0 ? 0b101 : 0b111;
}
}
}
}
#if defined(TCCR0B)
if (_timer == 0)
{
TCCR0B = prescalarbits;
}
else
#endif
#if defined(TCCR2B)
{
TCCR2B = prescalarbits;
}
#else
{
// dummy place holder to make the above ifdefs work
}
#endif
}
else
{
// two choices for the 16 bit timers: ck/1 or ck/64
ocr = F_CPU / frequency / 2 - 1;
prescalarbits = 0b001;
if (ocr > 0xffff)
{
ocr = F_CPU / frequency / 2 / 64 - 1;
prescalarbits = 0b011;
}
if (_timer == 1)
{
#if defined(TCCR1B)
TCCR1B = (TCCR1B & 0b11111000) | prescalarbits;
#endif
}
#if defined(TCCR3B)
else if (_timer == 3)
TCCR3B = (TCCR3B & 0b11111000) | prescalarbits;
#endif
#if defined(TCCR4B)
else if (_timer == 4)
TCCR4B = (TCCR4B & 0b11111000) | prescalarbits;
#endif
#if defined(TCCR5B)
else if (_timer == 5)
TCCR5B = (TCCR5B & 0b11111000) | prescalarbits;
#endif
}
// Calculate the toggle count
if (duration > 0)
{
toggle_count = 2 * frequency * duration / 1000;
}
else
{
toggle_count = -1;
}
// Set the OCR for the given timer,
// set the toggle count,
// then turn on the interrupts
switch (_timer)
{
#if defined(OCR0A) && defined(TIMSK0) && defined(OCIE0A)
case 0:
OCR0A = ocr;
timer0_toggle_count = toggle_count;
bitWrite(TIMSK0, OCIE0A, 1);
break;
#endif
case 1:
#if defined(OCR1A) && defined(TIMSK1) && defined(OCIE1A)
OCR1A = ocr;
timer1_toggle_count = toggle_count;
bitWrite(TIMSK1, OCIE1A, 1);
#elif defined(OCR1A) && defined(TIMSK) && defined(OCIE1A)
// this combination is for at least the ATmega32
OCR1A = ocr;
timer1_toggle_count = toggle_count;
bitWrite(TIMSK, OCIE1A, 1);
#endif
break;
#if defined(OCR2A) && defined(TIMSK2) && defined(OCIE2A)
case 2:
OCR2A = ocr;
timer2_toggle_count = toggle_count;
bitWrite(TIMSK2, OCIE2A, 1);
break;
#endif
#if defined(TIMSK3)
case 3:
OCR3A = ocr;
timer3_toggle_count = toggle_count;
bitWrite(TIMSK3, OCIE3A, 1);
break;
#endif
#if defined(TIMSK4)
case 4:
OCR4A = ocr;
timer4_toggle_count = toggle_count;
bitWrite(TIMSK4, OCIE4A, 1);
break;
#endif
#if defined(OCR5A) && defined(TIMSK5) && defined(OCIE5A)
case 5:
OCR5A = ocr;
timer5_toggle_count = toggle_count;
bitWrite(TIMSK5, OCIE5A, 1);
break;
#endif
}
}
}
// XXX: this function only works properly for timer 2 (the only one we use
// currently). for the others, it should end the tone, but won't restore
// proper PWM functionality for the timer.
void disableTimer(uint8_t _timer)
{
switch (_timer)
{
case 0:
#if defined(TIMSK0)
TIMSK0 = 0;
#elif defined(TIMSK)
TIMSK = 0; // atmega32
#endif
break;
#if defined(TIMSK1) && defined(OCIE1A)
case 1:
bitWrite(TIMSK1, OCIE1A, 0);
break;
#endif
case 2:
#if defined(TIMSK2) && defined(OCIE2A)
bitWrite(TIMSK2, OCIE2A, 0); // disable interrupt
#endif
#if defined(TCCR2A) && defined(WGM20)
TCCR2A = (1 << WGM20);
#endif
#if defined(TCCR2B) && defined(CS22)
TCCR2B = (TCCR2B & 0b11111000) | (1 << CS22);
#endif
#if defined(OCR2A)
OCR2A = 0;
#endif
break;
#if defined(TIMSK3)
case 3:
TIMSK3 = 0;
break;
#endif
#if defined(TIMSK4)
case 4:
TIMSK4 = 0;
break;
#endif
#if defined(TIMSK5)
case 5:
TIMSK5 = 0;
break;
#endif
}
}
// XXX: this function only works properly for timer 2 (the only one we use
// currently). for the others, it should end the tone, but won't restore
// proper PWM functionality for the timer.
void disableTimer(uint8_t _timer)
void noTone(uint8_t _pin)
{
int8_t _timer = -1;
for (int i = 0; i < AVAILABLE_TONE_PINS; i++)
{
if (tone_pins[i] == _pin)
{
_timer = pgm_read_byte(tone_pin_to_timer_PGM + i);
tone_pins[i] = 255;
}
}
disableTimer(_timer);
digitalWrite(_pin, 0);
}
#ifdef USE_TIMER0
ISR(TIMER0_COMPA_vect)
{
if (timer0_toggle_count != 0)
{
// toggle the pin
*timer0_pin_port ^= timer0_pin_mask;
if (timer0_toggle_count > 0)
timer0_toggle_count--;
}
else
{
disableTimer(0);
*timer0_pin_port &= ~(timer0_pin_mask); // keep pin low after stop
}
}
#endif
#ifdef USE_TIMER1
ISR(TIMER1_COMPA_vect)
{
if (timer1_toggle_count != 0)
{
// toggle the pin
*timer1_pin_port ^= timer1_pin_mask;
if (timer1_toggle_count > 0)
timer1_toggle_count--;
}
else
{
disableTimer(1);
*timer1_pin_port &= ~(timer1_pin_mask); // keep pin low after stop
}
}
#endif
#ifdef USE_TIMER2
ISR(TIMER2_COMPA_vect)
{
if (timer2_toggle_count != 0)
{
// toggle the pin
*timer2_pin_port ^= timer2_pin_mask;
if (timer2_toggle_count > 0)
timer2_toggle_count--;
}
else
{
// need to call noTone() so that the tone_pins[] entry is reset, so the
// timer gets initialized next time we call tone().
// XXX: this assumes timer 2 is always the first one used.
noTone(tone_pins[0]);
// disableTimer(2);
// *timer2_pin_port &= ~(timer2_pin_mask); // keep pin low after stop
}
}
#endif
#ifdef USE_TIMER3
ISR(TIMER3_COMPA_vect)
{
if (timer3_toggle_count != 0)
{
// toggle the pin
*timer3_pin_port ^= timer3_pin_mask;
if (timer3_toggle_count > 0)
timer3_toggle_count--;
}
else
{
disableTimer(3);
*timer3_pin_port &= ~(timer3_pin_mask); // keep pin low after stop
}
}
#endif
#ifdef USE_TIMER4
ISR(TIMER4_COMPA_vect)
{
if (timer4_toggle_count != 0)
{
// toggle the pin
*timer4_pin_port ^= timer4_pin_mask;
if (timer4_toggle_count > 0)
timer4_toggle_count--;
}
else
{
disableTimer(4);
*timer4_pin_port &= ~(timer4_pin_mask); // keep pin low after stop
}
}
#endif
#ifdef USE_TIMER5
ISR(TIMER5_COMPA_vect)
{
if (timer5_toggle_count != 0)
{
// toggle the pin
*timer5_pin_port ^= timer5_pin_mask;
if (timer5_toggle_count > 0)
timer5_toggle_count--;
}
else
{
disableTimer(5);
*timer5_pin_port &= ~(timer5_pin_mask); // keep pin low after stop
}
}
#endif
#endif // 0 [OLD CODE]
#endif // TONE_SUPPORTED