diff options
-rwxr-xr-x | cores/arduino/Tone.cpp | 515 | ||||
-rwxr-xr-x | cores/arduino/WProgram.h | 3 |
2 files changed, 518 insertions, 0 deletions
diff --git a/cores/arduino/Tone.cpp b/cores/arduino/Tone.cpp new file mode 100755 index 0000000..00ab730 --- /dev/null +++ b/cores/arduino/Tone.cpp @@ -0,0 +1,515 @@ +/* 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 + +*************************************************/ + +#include <avr/interrupt.h> +#include <avr/pgmspace.h> +#include <wiring.h> +#include <pins_arduino.h> + +#if defined(__AVR_ATmega8__) +#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(__AVR_ATmega1280__) +volatile long timer3_toggle_count; +volatile uint8_t *timer3_pin_port; +volatile uint8_t timer3_pin_mask; +volatile long timer4_toggle_count; +volatile uint8_t *timer4_pin_port; +volatile uint8_t timer4_pin_mask; +volatile long timer5_toggle_count; +volatile uint8_t *timer5_pin_port; +volatile uint8_t timer5_pin_mask; +#endif + + +#if defined(__AVR_ATmega1280__) + +#define AVAILABLE_TONE_PINS 5 + +// Leave timers 1, and zero to last. +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 2 + +const uint8_t PROGMEM tone_pin_to_timer_PGM[] = { 2, 1 }; +static uint8_t tone_pins[AVAILABLE_TONE_PINS] = { 255, 255 }; + +#else + +#define AVAILABLE_TONE_PINS 2 + +// 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 + + + +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(__AVR_ATmega8__) + 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 + + 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; + 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; + +#if defined(__AVR_ATmega1280__) + 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; + case 4: + // 16 bit timer + TCCR4A = 0; + TCCR4B = 0; + bitWrite(TCCR4B, WGM42, 1); + bitWrite(TCCR4B, CS40, 1); + timer4_pin_port = portOutputRegister(digitalPinToPort(_pin)); + timer4_pin_mask = digitalPinToBitMask(_pin); + break; + 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(__AVR_ATmega8__) + if (_timer == 0) + TCCR0B = prescalarbits; + else +#endif + TCCR2B = prescalarbits; + } + 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) + TCCR1B = (TCCR1B & 0b11111000) | prescalarbits; +#if defined(__AVR_ATmega1280__) + else if (_timer == 3) + TCCR3B = (TCCR3B & 0b11111000) | prescalarbits; + else if (_timer == 4) + TCCR4B = (TCCR4B & 0b11111000) | prescalarbits; + 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(__AVR_ATmega8__) + case 0: + OCR0A = ocr; + timer0_toggle_count = toggle_count; + bitWrite(TIMSK0, OCIE0A, 1); + break; +#endif + + case 1: + OCR1A = ocr; + timer1_toggle_count = toggle_count; + bitWrite(TIMSK1, OCIE1A, 1); + break; + case 2: + OCR2A = ocr; + timer2_toggle_count = toggle_count; + bitWrite(TIMSK2, OCIE2A, 1); + break; + +#if defined(__AVR_ATmega1280__) + case 3: + OCR3A = ocr; + timer3_toggle_count = toggle_count; + bitWrite(TIMSK3, OCIE3A, 1); + break; + case 4: + OCR4A = ocr; + timer4_toggle_count = toggle_count; + bitWrite(TIMSK4, OCIE4A, 1); + break; + case 5: + OCR5A = ocr; + timer5_toggle_count = toggle_count; + bitWrite(TIMSK5, OCIE5A, 1); + break; +#endif + + } + } +} + + +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; + } + } + + switch (_timer) + { +#if defined(__AVR_ATmega8__) + case 1: + bitWrite(TIMSK1, OCIE1A, 0); + break; + case 2: + bitWrite(TIMSK2, OCIE2A, 0); + break; + +#else + case 0: + TIMSK0 = 0; + break; + case 1: + TIMSK1 = 0; + break; + case 2: + TIMSK2 = 0; + break; +#endif + +#if defined(__AVR_ATmega1280__) + case 3: + TIMSK3 = 0; + break; + case 4: + TIMSK4 = 0; + break; + case 5: + TIMSK5 = 0; + break; +#endif + } + + digitalWrite(_pin, 0); +} + + +//#if !defined(__AVR_ATmega8__) +#if 0 +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 + { + TIMSK0 = 0; // disable the interrupt + *timer0_pin_port &= ~(timer0_pin_mask); // keep pin low after stop + } +} +#endif + + +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 + { + TIMSK1 = 0; // disable the interrupt + *timer1_pin_port &= ~(timer1_pin_mask); // keep pin low after stop + } +} + + +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 + { + TIMSK2 = 0; // disable the interrupt + *timer2_pin_port &= ~(timer2_pin_mask); // keep pin low after stop + } +} + + + +#if defined(__AVR_ATmega1280__) + +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 + { + TIMSK3 = 0; // disable the interrupt + *timer3_pin_port &= ~(timer3_pin_mask); // keep pin low after stop + } +} + +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 + { + TIMSK4 = 0; // disable the interrupt + *timer4_pin_port &= ~(timer4_pin_mask); // keep pin low after stop + } +} + +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 + { + TIMSK5 = 0; // disable the interrupt + *timer5_pin_port &= ~(timer5_pin_mask); // keep pin low after stop + } +} + +#endif diff --git a/cores/arduino/WProgram.h b/cores/arduino/WProgram.h index fc14923..f5d3e29 100755 --- a/cores/arduino/WProgram.h +++ b/cores/arduino/WProgram.h @@ -19,6 +19,9 @@ uint16_t makeWord(byte h, byte l); unsigned long pulseIn(uint8_t pin, uint8_t state, unsigned long timeout = 1000000L); +void tone(uint8_t _pin, unsigned int frequency, unsigned long duration = 0); +void noTone(uint8_t _pin); + // WMath prototypes long random(long); long random(long, long); |