From 0fe54d054fecc43ee7d230aa56be4cc21177a0d3 Mon Sep 17 00:00:00 2001 From: Reed Nightingale Date: Sat, 25 Apr 2020 22:32:17 -0700 Subject: [PATCH] Add toneAC2 library --- .../examples/toneAC2_demo/toneAC2_demo.pde | 27 +++++++ toneAC2/keywords.txt | 18 +++++ toneAC2/toneAC2.cpp | 64 ++++++++++++++++ toneAC2/toneAC2.h | 73 +++++++++++++++++++ 4 files changed, 182 insertions(+) create mode 100644 toneAC2/examples/toneAC2_demo/toneAC2_demo.pde create mode 100644 toneAC2/keywords.txt create mode 100644 toneAC2/toneAC2.cpp create mode 100644 toneAC2/toneAC2.h diff --git a/toneAC2/examples/toneAC2_demo/toneAC2_demo.pde b/toneAC2/examples/toneAC2_demo/toneAC2_demo.pde new file mode 100644 index 0000000..fa1b6e6 --- /dev/null +++ b/toneAC2/examples/toneAC2_demo/toneAC2_demo.pde @@ -0,0 +1,27 @@ +// --------------------------------------------------------------------------- +// Be sure to include an in-line 100 ohm resistor on one pin as you normally do when connecting a piezo or speaker. +// --------------------------------------------------------------------------- + +#include + +// Melody liberated from the toneMelody Arduino example sketch by Tom Igoe. +int melody[] = { 262, 196, 196, 220, 196, 0, 247, 262 }; +int noteDurations[] = { 4, 8, 8, 4, 4, 4, 4, 4 }; + +void setup() {} // Nothing to setup, just start playing! + +void loop() { + for (unsigned long freq = 125; freq <= 15000; freq += 10) { + toneAC2(2, 3, freq, 1); // Play the frequency (125 Hz to 15 kHz sweep in 10 Hz steps) for 1ms. + } + + delay(1000); // Wait a second. + + for (int thisNote = 0; thisNote < 8; thisNote++) { + int noteDuration = 1000/noteDurations[thisNote]; + toneAC2(2, 3, melody[thisNote], noteDuration, true); // Play thisNote at full volume for noteDuration in the background. + delay(noteDuration * 4 / 3); // Wait while the tone plays in the background, plus another 33% delay between notes. + } + + while(1); // Stop (so it doesn't repeat forever driving you crazy--you're welcome). +} diff --git a/toneAC2/keywords.txt b/toneAC2/keywords.txt new file mode 100644 index 0000000..9930b76 --- /dev/null +++ b/toneAC2/keywords.txt @@ -0,0 +1,18 @@ +################################### +# Syntax Coloring Map For toneAC2 +################################### + +################################### +# Datatypes (KEYWORD1) +################################### + +################################### +# Methods and Functions (KEYWORD2) +################################### + +toneAC2 KEYWORD2 +noToneAC2 KEYWORD2 + +################################### +# Constants (LITERAL1) +################################### diff --git a/toneAC2/toneAC2.cpp b/toneAC2/toneAC2.cpp new file mode 100644 index 0000000..7de7274 --- /dev/null +++ b/toneAC2/toneAC2.cpp @@ -0,0 +1,64 @@ +// --------------------------------------------------------------------------- +// Created by Tim Eckel - teckel@leethost.com +// Copyright 2015 License: GNU GPL v3 http://www.gnu.org/licenses/gpl-3.0.html +// +// See "toneAC2.h" for purpose, syntax, version history, links, and more. +// --------------------------------------------------------------------------- + +#include "toneAC2.h" + +unsigned long _tAC2_time; // Used to track end note with timer when playing note in the background. +volatile uint8_t *_pinMode1, *_pinMode2; // Pin modes. +uint8_t _pinMask1 = 0, _pinMask2 = 0; // Bitmask for pins. +volatile uint8_t *_pinOutput1, *_pinOutput2; // Output port registers for each pin. +int _tAC2_prescale[] = { 2, 16, 64, 128, 256, 512, 2048 }; // Prescaler. + +void toneAC2(uint8_t pin1, uint8_t pin2, unsigned int frequency, unsigned long length, uint8_t background) { + long top; + uint8_t prescaler; + + for (prescaler = 1; prescaler < 8; prescaler++) { // Find the appropriate prescaler + top = F_CPU / (long) frequency / (long) _tAC2_prescale[prescaler - 1] - 1; // Calculate the top. + if (top < 256) break; // Fits, break out of for loop. + } + if (top > 255) { noToneAC2(); return; } // Frequency is out of range, turn off sound and return. + + if (length > 0) _tAC2_time = millis() + length - 1; else _tAC2_time = 0xFFFFFFFF; // Set when the note should end, or play "forever". + + if (_pinMask1 == 0) { // This gets the port registers and bitmaps for the two pins and sets the pins to output mode. + _pinMask1 = digitalPinToBitMask(pin1); // Get the port register bitmask for pin 1. + _pinMask2 = digitalPinToBitMask(pin2); // Get the port register bitmask for pin 2. + _pinOutput1 = portOutputRegister(digitalPinToPort(pin1)); // Get the output port register for pin 1. + _pinOutput2 = portOutputRegister(digitalPinToPort(pin2)); // Get the output port register for pin 2. + _pinMode1 = (uint8_t *) portModeRegister(digitalPinToPort(pin1)); // Get the port mode register for pin 1. + _pinMode2 = (uint8_t *) portModeRegister(digitalPinToPort(pin2)); // Get the port mode register for pin 2. + *_pinMode1 |= _pinMask1; // Set pin 1 to Output mode. + *_pinMode2 |= _pinMask2; // Set pin 2 to Output mode. + } + + OCR2A = top; // Set the top. + if (TCNT2 > top) TCNT2 = top; // Counter over the top, put within range. + TCCR2B = _BV(WGM22) | prescaler; // Set Fast PWM and prescaler. + TCCR2A = _BV(WGM20) | _BV(WGM21); // Fast PWM and normal port operation, OC2A/OC2B disconnected. + TIMSK2 &= ~_BV(OCIE2A); // Stop timer 2 interrupt while we set the pin states. + if (*_pinOutput1 & _pinMask1) *_pinOutput2 &= ~_pinMask2; // Be sure pins are reversed. + else *_pinOutput2 |= _pinMask2; // Other half of making sure pins are reversed. + TIMSK2 |= _BV(OCIE2A); // Activate the timer interrupt. + + if (length > 0 && !background) { delay(length); noToneAC2(); } // Just a simple delay, doesn't return control till finished. +} + +void noToneAC2() { + TIMSK2 &= ~_BV(OCIE2A); // Remove the timer interrupt. + TCCR2B = _BV(CS22); // Default clock prescaler of 64. + TCCR2A = _BV(WGM20); // Set to defaults so PWM can work like normal (PWM, phase corrected, 8bit). + *_pinMode1 &= ~_pinMask1; // Set pin 1 to INPUT. + *_pinMode2 &= ~_pinMask2; // Set pin 2 to INPUT. + _pinMask1 = 0; // Flag so we know note is no longer playing. +} + +ISR(TIMER2_COMPA_vect) { // Timer interrupt vector. + if (millis() > _tAC2_time) noToneAC2(); // Check to see if it's time for the note to end. + *_pinOutput1 ^= _pinMask1; // Toggle the pin 1 state. + *_pinOutput2 ^= _pinMask2; // Toggle the pin 2 state. +} \ No newline at end of file diff --git a/toneAC2/toneAC2.h b/toneAC2/toneAC2.h new file mode 100644 index 0000000..1d244d6 --- /dev/null +++ b/toneAC2/toneAC2.h @@ -0,0 +1,73 @@ +// --------------------------------------------------------------------------- +// toneAC2 Library - v1.1 - 09/15/2015 +// +// AUTHOR/LICENSE: +// Created by Tim Eckel - teckel@leethost.com +// Copyright 2015 License: GNU GPL v3 http://www.gnu.org/licenses/gpl-3.0.html +// +// LINKS: +// Project home: https://bitbucket.org/teckel12/arduino-toneac/wiki/Home +// Blog: http://forum.arduino.cc/index.php?topic=142097.0 +// +// DISCLAIMER: +// This software is furnished "as is", without technical support, and with no +// warranty, express or implied, as to its usefulness for any purpose. +// +// PURPOSE: +// Replacement to the standard tone library with the advantage of nearly twice +// the volume, 800 bytes smaller compiled code size, and less stress on the +// speaker. This alternate version uses timer 2 and allows for flexible pin +// assignment. The primary version (toneAC) allows for higher frequencies, +// higher quality, and even smaller code size. However, toneAC is fixed to +// using the PWM timer 1 pins unlike toneAC2 which can use any two pins. Both +// exclusively use port registers for the fast and smallest code possible. +// +// USAGE: +// Connection is very similar to a piezo or standard speaker. Except, instead +// of connecting one speaker wire to ground you connect both speaker wires to +// Arduino pins. Unlike toneAC, with toneAC2 you can connect to any two pins. +// Just as usual when connecting a speaker, make sure you add an in-line 100 +// ohm resistor between one of the pins and the speaker wire. +// +// SYNTAX: +// toneAC2( pin1, pin2, frequency [, length [, background ]] ) - Play a note. +// Parameters: +// * pin1 - Pin to attach one of the speaker wires. +// * pin2 - Pin to attach the other speaker wire. +// * frequency - Play the specified frequency indefinitely, turn off with noToneAC2(). +// * length - [optional] Set the length to play in milliseconds. (default: 0 [forever], range: 0 to 2^32-1) +// * background - [optional] Play note in background or pause till finished? (default: false, values: true/false) +// noToneAC2() - Stop playing. +// +// HISTORY: +// 09/15/2015 v1.1 - Fix a potential race condition with _tAC2_time. Moved +// development to Bitbucket. +// +// 01/27/2013 v1.0 - Initial release. +// +// --------------------------------------------------------------------------- + +#ifndef toneAC2_h + #define toneAC2_h + + #if defined(ARDUINO) && ARDUINO >= 100 + #include + #else + #include + #endif + + // This doesn't currently work. Would require more work than simply doing this. + #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__) + #define TCCR2A TCCR2 + #define TCCR2B TCCR2 + #define TIMSK2 TIMSK + #define COM2A1 COM21 + #define COM2A0 COM20 + #define OCIE2A OCIE2 + #define OCR2A OCR2 + #define TIMER2_COMPA_vect TIMER2_COMP_vect + #endif + + void toneAC2(uint8_t pin1, uint8_t pin2, unsigned int frequency = 0, unsigned long length = 0, uint8_t background = false); + void noToneAC2(); +#endif \ No newline at end of file