153 lines
4.7 KiB
C++
153 lines
4.7 KiB
C++
/* Teensyduino Core Library
|
|
* http://www.pjrc.com/teensy/
|
|
* Copyright (c) 2017 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 uint8_t *tone_reg;
|
|
static uint8_t tone_pin=255;
|
|
static float tone_usec=0.0;
|
|
static uint32_t tone_new_count=0;
|
|
IntervalTimer tone_timer;
|
|
|
|
void tone_interrupt(void);
|
|
|
|
#if defined(KINETISK)
|
|
#define TONE_CLEAR_PIN tone_reg[0] = 1
|
|
#define TONE_TOGGLE_PIN tone_reg[128] = 1
|
|
#define TONE_OUTPUT_PIN tone_reg[384] = 1
|
|
|
|
#elif defined(KINETISL)
|
|
static uint8_t tone_mask;
|
|
#define TONE_CLEAR_PIN tone_reg[0] = tone_mask
|
|
#define TONE_TOGGLE_PIN tone_reg[4] = tone_mask
|
|
#define TONE_OUTPUT_PIN __disable_irq(); tone_reg[12] |= tone_mask; __enable_irq()
|
|
|
|
#endif
|
|
|
|
void tone(uint8_t pin, uint16_t frequency, uint32_t duration)
|
|
{
|
|
uint32_t count;
|
|
volatile uint32_t *config;
|
|
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;
|
|
config = 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 = portClearRegister(pin);
|
|
#if defined(KINETISL)
|
|
tone_mask = digitalPinToBitMask(pin);
|
|
#endif
|
|
TONE_CLEAR_PIN; // clear pin
|
|
TONE_OUTPUT_PIN; // output mode;
|
|
*config = PORT_PCR_SRE | PORT_PCR_DSE | PORT_PCR_MUX(1);
|
|
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();
|
|
}
|
|
|
|
|
|
|