| @@ -0,0 +1,143 @@ | |||
| /* Teensyduino Core Library | |||
| * http://www.pjrc.com/teensy/ | |||
| * Copyright (c) 2019 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: | |||
| * | |||
| * 1. The above copyright notice and this permission notice shall be | |||
| * included in all copies or substantial portions of the Software. | |||
| * | |||
| * 2. If the Software is incorporated into a build system that allows | |||
| * selection among a list of target devices, then similar target | |||
| * devices manufactured by PJRC.COM must be included in the list of | |||
| * target devices and selectable in the same manner. | |||
| * | |||
| * 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 <Arduino.h> | |||
| // IntervalTimer based tone. This allows tone() to share the timers with other | |||
| // libraries, rather than permanently hogging one PIT timer even for projects | |||
| // which never use tone(). Someday this single-tone implementation might be | |||
| // changed to allow multiple simultaneous tones. | |||
| static uint32_t tone_toggle_count; | |||
| static volatile uint32_t *tone_reg; | |||
| static uint32_t tone_mask; | |||
| static float tone_usec=0.0; | |||
| static uint32_t tone_new_count=0; | |||
| static IntervalTimer tone_timer; | |||
| static uint8_t tone_pin=255; | |||
| void tone_interrupt(void); | |||
| #define TONE_CLEAR_PIN (tone_reg[34] = tone_mask) | |||
| #define TONE_TOGGLE_PIN (tone_reg[35] = tone_mask) | |||
| #define TONE_OUTPUT_PIN (tone_reg[1] |= tone_mask) | |||
| void tone(uint8_t pin, uint16_t frequency, uint32_t duration) | |||
| { | |||
| uint32_t count; | |||
| volatile uint32_t *muxreg; | |||
| float usec; | |||
| if (pin >= CORE_NUM_DIGITAL) return; | |||
| if (duration) { | |||
| count = (frequency * duration / 1000) * 2; | |||
| if (!(count & 1)) count++; // always full waveform cycles | |||
| } else { | |||
| count = 0xFFFFFFFD; | |||
| } | |||
| if (frequency < 1) frequency = 1; // minimum is 1 Hz | |||
| usec = (float)500000.0 / (float)frequency; | |||
| muxreg = portConfigRegister(pin); | |||
| // TODO: IntervalTimer really needs an API to disable and enable | |||
| // the interrupt on a single timer. | |||
| __disable_irq(); | |||
| if (pin == tone_pin) { | |||
| // changing a pin which is already playing a tone | |||
| if (usec == tone_usec) { | |||
| // same frequency, so just change the duration | |||
| tone_toggle_count = (tone_toggle_count & 1) + count - 1; | |||
| } else { | |||
| // different frequency, reduce duration to only the | |||
| // remainder of its current cycle, and configure for | |||
| // the transition to the new frequency when the | |||
| // current cycle completes | |||
| tone_usec = usec; | |||
| tone_new_count = count; | |||
| tone_toggle_count = (tone_toggle_count & 1); | |||
| } | |||
| } else { | |||
| // if playing on a different pin, immediately stop, even mid-cycle :-( | |||
| if (tone_pin < CORE_NUM_DIGITAL) { | |||
| TONE_CLEAR_PIN; // clear pin | |||
| } | |||
| // configure the new tone to play | |||
| tone_pin = pin; | |||
| tone_reg = portOutputRegister(pin); | |||
| tone_mask = digitalPinToBitMask(pin); | |||
| TONE_CLEAR_PIN; // clear pin | |||
| TONE_OUTPUT_PIN; // output mode; | |||
| *muxreg = 5; | |||
| // TODO: configure pad register | |||
| tone_toggle_count = count; | |||
| tone_usec = usec; | |||
| tone_timer.begin(tone_interrupt, usec); | |||
| } | |||
| __enable_irq(); | |||
| } | |||
| void tone_interrupt(void) | |||
| { | |||
| if (tone_toggle_count) { // odd = rising edge, even = falling edge | |||
| // not the end | |||
| TONE_TOGGLE_PIN; // toggle | |||
| tone_toggle_count--; | |||
| if (tone_toggle_count == 0xFFFFFFFB) tone_toggle_count = 0xFFFFFFFD; | |||
| } else { | |||
| // this transition completes the tone | |||
| TONE_CLEAR_PIN; // clear | |||
| if (tone_new_count > 0) { | |||
| // begin playing a new tone | |||
| tone_timer.begin(tone_interrupt, tone_usec); | |||
| tone_toggle_count = tone_new_count; | |||
| tone_new_count = 0; | |||
| } else { | |||
| // finished playing | |||
| tone_timer.end(); | |||
| tone_pin = 255; | |||
| } | |||
| } | |||
| } | |||
| void noTone(uint8_t pin) | |||
| { | |||
| if (pin >= CORE_NUM_DIGITAL) return; | |||
| __disable_irq(); | |||
| if (pin == tone_pin) { | |||
| tone_timer.end(); | |||
| TONE_CLEAR_PIN; // clear | |||
| tone_pin = 255; | |||
| } | |||
| __enable_irq(); | |||
| } | |||