/* Tone generation for the Teensy and Teensy++ * http://www.pjrc.com/teensy/ * Copyright (c) 2010 PJRC.COM, LLC * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include "wiring.h" #include "core_pins.h" #include "pins_arduino.h" static uint8_t timer_acquired = 0; static uint8_t *tone1_reg = (uint8_t *)0; static uint8_t tone1_mask = 0; static uint16_t tone1_inc = 0; static uint32_t tone1_count = 0; static uint8_t *tone2_reg = (uint8_t *)0; static uint8_t tone2_mask = 0; static uint16_t tone2_inc = 0; static uint32_t tone2_count = 0; static uint8_t *tone3_reg = (uint8_t *)0; static uint8_t tone3_mask = 0; static uint16_t tone3_inc = 0; static uint32_t tone3_count = 0; #define MAX_FREQ (F_CPU / 16 / 25) #define MIN_FREQ (F_CPU / 16 / 65535 + 1) #define PIN_REG_AND_MASK_LOOKUP(pin, reg, mask) \ asm volatile( \ "lsl %2" "\n\t" \ "add %A3, %2" "\n\t" \ "adc %B3, __zero_reg__" "\n\n" \ "lpm %1, Z+" "\n\t" \ "lpm %A0, Z" "\n\t" \ "ldi %B0, 0" "\n" \ : "=z" (reg), "=r" (mask), "+r" (pin) \ : "z" (digital_pin_table_PGM), "2" (pin)) #if defined(__AVR_ATmega32U4__) //#define TONE_USE_TIMER1 #define TONE_USE_TIMER3 #elif defined(__AVR_AT90USB1286__) //#define TONE_USE_TIMER1 #define TONE_USE_TIMER3 #elif defined(__AVR_AT90USB162__) #define TONE_USE_TIMER1 #elif defined(__AVR_AT90USB646__) //#define TONE_USE_TIMER1 #define TONE_USE_TIMER3 #endif #ifdef TONE_USE_TIMER3 #define TIMSKx TIMSK3 #define OCIExA OCIE3A #define OCIExB OCIE3B #define OCIExC OCIE3C #define TCCRxA TCCR3A #define WGMx0 WGM30 #define TCCRxB TCCR3B #define CSx1 CS31 #define TCNTx TCNT3 #define OCRxA OCR3A #define OCRxB OCR3B #define OCRxC OCR3C #define TIFRx TIFR3 #define OCFxA OCF3A #define OCFxB OCF3B #define OCFxC OCF3C #define VECTxA TIMER3_COMPA_vect #define VECTxB TIMER3_COMPB_vect #define VECTxC TIMER3_COMPC_vect #endif #ifdef TONE_USE_TIMER1 #define TIMSKx TIMSK1 #define OCIExA OCIE1A #define OCIExB OCIE1B #define OCIExC OCIE1C #define TCCRxA TCCR1A #define WGMx0 WGM10 #define TCCRxB TCCR1B #define CSx1 CS11 #define TCNTx TCNT1 #define OCRxA OCR1A #define OCRxB OCR1B #define OCRxC OCR1C #define TIFRx TIFR1 #define OCFxA OCF1A #define OCFxB OCF1B #define OCFxC OCF1C #define VECTxA TIMER1_COMPA_vect #define VECTxB TIMER1_COMPB_vect #define VECTxC TIMER1_COMPC_vect #endif void tone(uint8_t pin, uint16_t frequency, uint32_t duration) { uint8_t *reg; uint8_t mask; uint16_t inc; uint32_t count; if (pin >= CORE_NUM_TOTAL_PINS) return; if (frequency < MIN_FREQ) { frequency = MIN_FREQ; } else if (frequency > MAX_FREQ) { frequency = MAX_FREQ; } inc = (F_CPU / 16 + frequency / 2) / frequency; if (duration) { count = duration * frequency / 500; } else { count = 0; } if (!timer_acquired) { TIMSKx = 0; // disable all interrupts TCCRxA = 0; TCCRxB = (1<= CORE_NUM_TOTAL_PINS) return; PIN_REG_AND_MASK_LOOKUP(pin, reg, mask); if (tone1_mask == mask && tone1_reg == reg) { TIMSKx &= ~(1< 0) { if ((--tone1_count) == 0) { *(tone1_reg + 2) &= ~tone1_mask; TIMSKx &= ~(1< 0) { if ((--tone2_count) == 0) { *(tone2_reg + 2) &= ~tone2_mask; TIMSKx &= ~(1< 0) { if ((--tone3_count) == 0) { *(tone3_reg + 2) &= ~tone3_mask; TIMSKx &= ~(1<