Browse Source

Add tone function

teensy4-core
PaulStoffregen 6 years ago
parent
commit
eb3d0484b1
1 changed files with 143 additions and 0 deletions
  1. +143
    -0
      teensy4/Tone.cpp

+ 143
- 0
teensy4/Tone.cpp View File

@@ -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();
}




Loading…
Cancel
Save