I’m trying to port the toneAC library so i can drive a piezo utilizing differential drive. I’ve started by replacing the _BV macro and adding application.h but don’t know how to continue. I haven’t found too many resources on how to do this so I would appreciate some help. It’s a short library so i’ll post what i have here. On the other hand, if you know of a better way to use differential drive with a piezo (or resources on how to port a library), please let me know. Thanks for any help you may be able to provide!
.h
// PURPOSE:
// Replacement to the standard tone library with the advantage of nearly twice
// the volume, higher frequencies (even if running at a lower clock speed),
// much higher quality (no clicking), nearly 2k smaller compiled code and less
// stress on the speaker. Disadvantages are that it must use certain pins and
// it uses two pins instead of one. But, if you're flexible with your pin
// choices, this is a great upgrade. It also uses timer 1 instead of timer 2,
// which may free up a conflict you have with the tone library. It exclusively
// uses port registers for the fastest and smallest code possible.
//
// USAGE:
// Should work with most Arduino and compatible systems including Teensy 2.0.
// Designed with Arduino 1.02. You must use the two PWM pins for timer 1 on
// your Arduino (see CONNECTION below). Connect your piezo or speaker up the
// listed pins and use the toneAC() command instad of tone() for the benefits
// mentioned above.
//
// CONNECTION:
// ATmega328, ATmega128, Uno, etc. - pins 9 & 10
// ATmega2560, ATmega1280, Mega - pins 11 & 12
// Teensy 2.0 (maybe Leonardo?) - pins 14 & 15
//
// SYNTAX:
// toneAC(frequency) - Play the specified frequency indefinitely.
// toneAC(frequency, volume) - You can optionally select a certain volume level (0 - 100).
// toneAC(frequency, volume, length) - Also optionally set the length to play in milliseconds.
// toneAC() - Stop output.
// noToneAC() - Same as toneAC().
//
// HISTORY:
// 01/11/2013 v1.0 - First release.
//
// ---------------------------------------------------------------------------
#ifndef toneAC_h
#define toneAC_h
#if defined (SPARK)
#include "application.h"
#else
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#endif
#if defined (__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) || defined(__AVR_ATmega1284P__)
#define PWMT1AMASK DDB5
#define PWMT1BMASK DDB6
#else
#define PWMT1AMASK DDB1
#define PWMT1BMASK DDB2
#endif
void toneAC(unsigned long frequency = 0, uint8_t volume = 100, unsigned long length = 0);
void noToneAC();
#endif
.cpp
#include "toneAC.h"
void toneAC(unsigned long frequency, uint8_t volume, unsigned long length) {
if (frequency == 0 || volume == 0) { noToneAC(); return; } // If frequency or volume is 0, turn off sound and return.
if (volume > 199) volume = 199; // Make sure volume is in range (100 is full volume, 50% duty).
DDRB |= (1 << (PWMT1AMASK)) | (1 << (PWMT1BMASK)); // Set timer 1 PWM pins to OUTPUT (because analogWrite does it too).
uint8_t prescaler = (1 << (CS10)); // Try using prescaler 1 first.
unsigned long limit = F_CPU / frequency / 2 - 1; // Calculate the upper limit.
if (limit > 65535) { // If not in the range for prescaler 1, use prescaler 64 (below 123 Hz @ 16 MHz).
limit = F_CPU / frequency / 2 / 64 - 1; // Calculate the upper limit using prescaler 64.
prescaler |= (1 << (CS11)); // Add the prescaler bit.
}
unsigned int duty = max(limit * (unsigned long) volume / 200, 1); // Calculate the duty cycle (volume).
ICR1 = limit; // Upper limit.
TCCR1B = (1 << (WGM13)) | prescaler; // PWM, Phase and Frequency Corrected (ICR1) and prescaler.
OCR1A = OCR1B = duty; // Set the duty cycle (volume).
TCCR1A = (1 << (COM1A1)) | (1 << (COM1B1)) | (1 << (COM1B0)); // Inverted / none-inverted mode (AC).
if (length) { delay(length); noToneAC(); } // Just a simple delay, doesn't return control till finished.
}
void noToneAC() {
TCCR1B = (1 << (CS11)); // Default clock prescaler of 8.
TCCR1A = (1 << (WGM10)); // Set to defaults so PWM can work like normal (PWM, phase corrected, 8bit).
PORTB &= ~(1 << (PWMT1AMASK)); // Set timer 1 PWM pins to LOW.
PORTB &= ~(1 << (PWMT1BMASK));
}