Add Beep classes for playing simple tones

Also added a BeepDemo example sketch and modified the ArduBreakout
example sketch to use the BeepPin1 class instead of Arduino tone().
This commit is contained in:
Scott Allen 2018-02-19 16:09:42 -05:00
parent 460e768ea9
commit 53ea8188d5
8 changed files with 704 additions and 23 deletions

View File

@ -8,7 +8,7 @@ Software License Agreements
Licensed under the BSD 3-clause license:
Arduboy2 library:
Copyright (c) 2016-2017, Scott Allen
Copyright (c) 2016-2018, Scott Allen
All rights reserved.
The Arduboy2 library was forked from the Arduboy library:
@ -134,6 +134,12 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
-------------------------------------------------------------------------------
Placed in the public domain:
BeepDemo example sketch:
By Scott Allen
===============================================================================
\endverbatim
*/

View File

@ -129,11 +129,19 @@ Arduboy2 arduboy;
arduboy.audio.off();
```
### Simple tone generation
The *BeepPin1* and *BeepPin2* classes are available to generate simple square wave tones using speaker pin 1 and speaker pin 2 respectively. These classes are documented in file *Arduboy2Beep.h*. Also, *BeepDemo* is included as one of the example sketches, which demonstrates basic use.
NOTE: These functions will not work with a DevKit Arduboy because the speaker pins used cannot be directly controlled by a timer/counter. "Dummy" functions are provided so a sketch will compile and work properly but no sound will be produced.
### Ways to make more code space available to sketches
#### Sound effects and music
If you want your sketch to have sound, then using the *ArduboyTones* library will be more code efficient than using *ArduboyPlaytune* or most other sound libraries compatible with the Arduboy. *ArduboyTones* even produces less code than the [Arduino built in *tone()* function](https://www.arduino.cc/en/Reference/Tone). You'll have to decide on the appropriate library or functions you use to generate sound, based on the features required and how much memory you want it to use.
If all you want is to play single tones, using the built in *BeepPin1* or *BeepPin2* classes will be very efficient.
If you want to be able to play sequences of tones or background music, using the [*ArduboyTones*](https://github.com/MLXXXp/ArduboyTones) library will be more code efficient than using [*ArduboyPlaytune*](https://github.com/Arduboy/ArduboyPlayTune) or most other sound libraries compatible with the Arduboy. *ArduboyTones* even produces less code than the [Arduino built in *tone()* function](https://www.arduino.cc/en/Reference/Tone). You'll have to decide on the appropriate library or functions you use to generate sound, based on the features required and how much memory you want it to use.
#### Remove the text functions

View File

@ -17,7 +17,9 @@
#define EE_FILE 2
Arduboy2 arduboy;
BeepPin1 beep;
const unsigned int FRAME_RATE = 40; // Frame rate in frames per second
const unsigned int COLUMNS = 13; //Columns of bricks
const unsigned int ROWS = 4; //Rows of bricks
int dx = -1; //Initial movement of ball
@ -57,7 +59,8 @@ byte tick;
void setup()
{
arduboy.begin();
arduboy.setFrameRate(40);
beep.begin();
arduboy.setFrameRate(FRAME_RATE);
arduboy.initRandomSeed();
}
@ -67,6 +70,9 @@ void loop()
if (!(arduboy.nextFrame()))
return;
// Handle the timing and stopping of tones
beep.timer();
//Title screen loop switches from title screen
//and high scores until FIRE is pressed
while (!start)
@ -190,7 +196,7 @@ void moveBall()
yb=60;
released = false;
lives--;
playTone(175, 250);
playToneTimed(175, 500);
if (random(0, 2) == 0)
{
dx = 1;
@ -347,7 +353,7 @@ void drawGameOver()
arduboy.print("Score: ");
arduboy.print(score);
arduboy.display();
delay(4000);
arduboy.delayShort(4000);
}
void pause()
@ -359,7 +365,7 @@ void pause()
arduboy.display();
while (paused)
{
delay(150);
arduboy.delayShort(150);
//Unpause if FIRE is pressed
pad2 = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
if (pad2 == true && oldpad2 == false && released)
@ -410,7 +416,7 @@ boolean pollFireButton(int n)
{
for(int i = 0; i < n; i++)
{
delay(15);
arduboy.delayShort(15);
pad = arduboy.pressed(A_BUTTON) || arduboy.pressed(B_BUTTON);
if(pad == true && oldpad == false)
{
@ -548,14 +554,14 @@ void enterInitials()
}
arduboy.drawLine(56, 28, 88, 28, 0);
arduboy.drawLine(56 + (index*8), 28, 56 + (index*8) + 6, 28, 1);
delay(150);
arduboy.delayShort(70);
if (arduboy.pressed(LEFT_BUTTON) || arduboy.pressed(B_BUTTON))
{
if (index > 0)
{
index--;
playTone(1046, 250);
playToneTimed(1046, 80);
}
}
@ -564,14 +570,14 @@ void enterInitials()
if (index < 2)
{
index++;
playTone(1046, 250);
playToneTimed(1046, 80);
}
}
if (arduboy.pressed(DOWN_BUTTON))
if (arduboy.pressed(UP_BUTTON))
{
initials[index]++;
playTone(523, 250);
playToneTimed(523, 80);
// A-Z 0-9 :-? !-/ ' '
if (initials[index] == '0')
{
@ -591,10 +597,10 @@ void enterInitials()
}
}
if (arduboy.pressed(UP_BUTTON))
if (arduboy.pressed(DOWN_BUTTON))
{
initials[index]--;
playTone(523, 250);
playToneTimed(523, 80);
if (initials[index] == ' ') {
initials[index] = '?';
}
@ -611,12 +617,11 @@ void enterInitials()
if (arduboy.pressed(A_BUTTON))
{
playToneTimed(1046, 80);
if (index < 2)
{
index++;
playTone(1046, 250);
} else {
playTone(1046, 250);
return;
}
}
@ -693,12 +698,19 @@ void enterHighScore(byte file)
}
}
// Wrap the Arduino tone() function so that the pin doesn't have to be
// specified each time. Also, don't play if audio is set to off.
void playTone(unsigned int frequency, unsigned long duration)
// Play a tone at the specified frequency for the specified duration.
void playTone(unsigned int frequency, unsigned int duration)
{
if (arduboy.audio.enabled() == true)
beep.tone(beep.freq(frequency), duration / (1000 / FRAME_RATE));
}
// Play a tone at the specified frequency for the specified duration using
// a delay to time the tone.
// Used when beep.timer() isn't being called.
void playToneTimed(unsigned int frequency, unsigned int duration)
{
tone(PIN_SPEAKER_1, frequency, duration);
}
beep.tone(beep.freq(frequency));
arduboy.delayShort(duration);
beep.noTone();
}

View File

@ -0,0 +1,129 @@
/*
This sketch provides an example of using the Arduboy2 library's BeepPin1 class
to play simple tones.
*/
/*
To the extent possible under law, Scott Allen has waived all copyright and
related or neighboring rights to this BeepDemo program.
*/
// Comments are only provided for code dealing with tone generation or control.
#include <Arduboy2.h>
// There's no need to #include <Arduboy2Beep.h>
// It will be included in Arduboy2.h
Arduboy2 arduboy;
BeepPin1 beep; // Create a class instance for speaker pin 1
//BeepPin2 beep; // For speaker pin 2, use this line instead of the line above
int objectX = 0;
char displayText[60];
void setup() {
arduboy.begin();
arduboy.setFrameRate(25);
beep.begin(); // Set up the hardware for playing tones
commandText("none - Press buttons");
}
void loop() {
if (!arduboy.nextFrame()) {
return;
}
// The timer() function is called once per frame, so duration values will be
// the number of frames that the tone plays for.
// At 25 frames per second each frame will be 40ms.
beep.timer(); // handle tone duration
arduboy.pollButtons();
if (arduboy.justPressed(LEFT_BUTTON)) {
// Play a 523.251Hz tone (piano note C5) for 5 frames (200ms at 25 FPS)
// beep.freq(523.251) is used to convert 523.251Hz to the required count
beep.tone(beep.freq(523.251), 5);
commandText("beep.tone(\n beep.freq(523.251),\n 5)");
}
if (arduboy.justPressed(UP_BUTTON)) {
// Play a 587.330Hz tone (piano note D5) for 15 frames (600ms at 25 FPS)
beep.tone(beep.freq(587.330), 15);
commandText("beep.tone(\n beep.freq(587.330),\n 15)");
}
if (arduboy.justPressed(RIGHT_BUTTON)) {
// Play a 659.255Hz tone (piano note E5) for 50 frames (2s at 25 FPS)
beep.tone(beep.freq(659.255), 50);
commandText("beep.tone(\n beep.freq(659.255),\n 50)");
}
if (arduboy.justPressed(DOWN_BUTTON)) {
// Play a 698.456Hz tone (piano note F5) until stopped
// or replaced by another tone
beep.tone(beep.freq(698.456));
commandText("beep.tone(\n beep.freq(698.456))");
}
if (arduboy.justPressed(A_BUTTON)) {
// For short tones with a duration less than a frame time,
// or when timer() isn't being used, such as in a menu,
// a continuous tone can be played and then stopped after a delay
// but note that no other work will be done during the delay.
beep.tone(beep.freq(1000)); // Play a 1000Hz tone until stopped
arduboy.delayShort(30); // Delay for 30ms
beep.noTone(); // Stop the tone
commandText("beep.tone(\n beep.freq(1000))\n(delay 30ms)\nbeep.noTone()");
}
if (arduboy.justPressed(B_BUTTON)) {
beep.noTone(); // Stop the tone if one is playing
commandText("beep.noTone()");
}
arduboy.println(F("Last command:"));
arduboy.print(displayText);
// The Arduboy2 class's audio subclass controls sound muting.
// For this sketch, mute or unmute can be set using the "System Control"
// start up feature. (Hold B while powering up then, while still holding B,
// press UP to enable sound or DOWN for mute.)
if (!arduboy.audio.enabled()) {
arduboy.setCursor(22, 40);
arduboy.print(F("Sound is MUTED"));
}
arduboy.setCursor(0, 48);
// The "duration" variable can be tested for non-zero to determine if a
// timed tone is currently playing.
if (beep.duration != 0) {
arduboy.print(F("A tone is playing"));
}
else {
arduboy.print(F("Continuous tone or\nno tone playing"));
}
arduboy.drawRect(objectX, 40, 6, 6);
if (++objectX == WIDTH - 6) {
objectX = 0;
}
arduboy.display(CLEAR_BUFFER);
}
void commandText(const char* text) {
strncpy(displayText, text, sizeof displayText);
displayText[sizeof displayText - 1] = '\0';
}

View File

@ -8,6 +8,8 @@
Arduboy2 KEYWORD1
Arduboy2Base KEYWORD1
BeepPin1 KEYWORD1
BeepPin2 KEYWORD1
Sprites KEYWORD1
#######################################
@ -98,6 +100,12 @@ writeShowUnitNameFlag KEYWORD2
writeUnitID KEYWORD2
writeUnitName KEYWORD2
# Arduboy2Beep classes
freq KEYWORD2
noTone KEYWORD2
timer KEYWORD2
tone KEYWORD2
# Sprites class
drawErase KEYWORD2
drawExternalMask KEYWORD2

View File

@ -10,6 +10,7 @@
#include <Arduino.h>
#include <EEPROM.h>
#include "Arduboy2Core.h"
#include "Arduboy2Beep.h"
#include "Sprites.h"
#include <Print.h>
#include <limits.h>

155
src/Arduboy2Beep.cpp Normal file
View File

@ -0,0 +1,155 @@
/**
* @file Arduboy2Beep.cpp
* \brief
* Classes to generate simple square wave tones on the Arduboy speaker pins.
*/
#include <Arduino.h>
#include "Arduboy2Beep.h"
#ifndef AB_DEVKIT
// Speaker pin 1, Timer 3A, Port C bit 6, Arduino pin 5
uint8_t BeepPin1::duration = 0;
void BeepPin1::begin()
{
TCCR3A = 0;
TCCR3B = (bit(WGM32) | bit(CS31)); // CTC mode. Divide by 8 clock prescale
}
void BeepPin1::tone(uint16_t count)
{
tone(count, 0);
}
void BeepPin1::tone(uint16_t count, uint8_t dur)
{
duration = dur;
TCCR3A = bit(COM3A0); // set toggle on compare mode (which connects the pin)
OCR3A = count; // load the count (16 bits), which determines the frequency
}
void BeepPin1::timer()
{
if (duration && (--duration == 0)) {
TCCR3A = 0; // set normal mode (which disconnects the pin)
}
}
void BeepPin1::noTone()
{
duration = 0;
TCCR3A = 0; // set normal mode (which disconnects the pin)
}
// Speaker pin 2, Timer 4A, Port C bit 7, Arduino pin 13
uint8_t BeepPin2::duration = 0;
void BeepPin2::begin()
{
TCCR4A = 0; // normal mode. Disable PWM
TCCR4B = bit(CS43); // divide by 128 clock prescale
TCCR4D = 0; // normal mode
TC4H = 0; // toggle pin at count = 0
OCR4A = 0; // "
}
void BeepPin2::tone(uint16_t count)
{
tone(count, 0);
}
void BeepPin2::tone(uint16_t count, uint8_t dur)
{
duration = dur;
TCCR4A = bit(COM4A0); // set toggle on compare mode (which connects the pin)
TC4H = highByte(count); // load the count (10 bits),
OCR4C = lowByte(count); // which determines the frequency
}
void BeepPin2::timer()
{
if (duration && (--duration == 0)) {
TCCR4A = 0; // set normal mode (which disconnects the pin)
}
}
void BeepPin2::noTone()
{
duration = 0;
TCCR4A = 0; // set normal mode (which disconnects the pin)
}
#else /* AB_DEVKIT */
// *** The pins used for the speaker on the DevKit cannot be directly
// controlled by a timer/counter. The following "dummy" functions will
// compile and operate properly but no sound will be produced
uint8_t BeepPin1::duration = 0;
void BeepPin1::begin()
{
}
void BeepPin1::tone(uint16_t count)
{
tone(count, 0);
}
void BeepPin1::tone(uint16_t count, uint8_t dur)
{
(void) count; // parameter not used
duration = dur;
}
void BeepPin1::timer()
{
if (duration) {
--duration;
}
}
void BeepPin1::noTone()
{
duration = 0;
}
uint8_t BeepPin2::duration = 0;
void BeepPin2::begin()
{
}
void BeepPin2::tone(uint16_t count)
{
tone(count, 0);
}
void BeepPin2::tone(uint16_t count, uint8_t dur)
{
(void) count; // parameter not used
duration = dur;
}
void BeepPin2::timer()
{
if (duration) {
--duration;
}
}
void BeepPin2::noTone()
{
duration = 0;
}
#endif

362
src/Arduboy2Beep.h Normal file
View File

@ -0,0 +1,362 @@
/**
* @file Arduboy2Beep.h
* \brief
* Classes to generate simple square wave tones on the Arduboy speaker pins.
*/
#ifndef ARDUBOY2_BEEP_H
#define ARDUBOY2_BEEP_H
/** \brief
* Play simple square wave tones using speaker pin 1.
*
* \note
* Class `BeepPin2` provides identical functions for playing tones on speaker
* pin 2. Both classes can be used in the same sketch to allow playing
* two tones at the same time. To do this, the `begin()` and `timer()`
* functions of both classes must be used.
*
* \details
* This class can be used to play square wave tones on speaker pin 1.
* The functions are designed to produce very small and efficient code.
*
* A tone can be set to play for a given duration, or continuously until
* stopped or replaced by a new tone. No interrupts are used. A tone is
* generated by a hardware timer/counter directly toggling the pin,
* so once started, no CPU cycles are used to actually play the tone.
* The program continues to run while a tone is playing. However, a small
* amount of code is required to time and stop a tone after a given duration.
*
* Tone frequencies can range from 15.26Hz to 1000000Hz.
*
* Although there's no specific code to handle mute control, the
* `Arduboy2Audio` class will work since it has code to mute sound by setting
* the speaker pins to input mode and unmute by setting the pins as outputs.
* The `BeepPin1` class doesn't interfere with this operation.
*
* In order to avoid needing to use interrupts, the duration of tones is timed
* by calling the `timer()` function continuously at a fixed interval.
* The duration of a tone is given by specifying the number of times `timer()`
* will be called before stopping the tone.
*
* For sketches that use `Arduboy2::nextFrame()`, or some other method to
* generate frames at a fixed rate, `timer()` can be called once per frame.
* Tone durations will then be given as the number of frames to play the tone
* for. For example, with a rate of 60 frames per second a duration of 30
* would be used to play a tone for half a second.
*
* The variable named `#duration` is the counter that times the duration of a
* tone. A sketch can determine if a tone is currently playing by testing if
* the `#duration` variable is non-zero (assuming it's a timed tone, not a
* continuous tone).
*
* To keep the code small and efficient, the frequency of a tone is specified
* by the actual count value to be loaded into to timer/counter peripheral.
* The frequency will be determined by the count provided and the clock rate
* of the timer/counter. In order to allow a tone's frequency to be specified
* in hertz (cycles per second) the `freq()` helper function is provided,
* which converts a given frequency to the required count value.
*
* NOTE that it is intended that `freq()` only be called with constant values.
* If `freq()` is called with a variable, code to perform floating point math
* will be included in the sketch, which will likely greatly increase the
* sketch's code size unless the sketch also uses floating point math for
* other purposes.
*
* The formulas for frequency/count conversion are:
*
* count=(1000000/frequency)-1
* frequency=1000000/(count+1)
*
* Counts must be between 0 and 65535.
*
* All members of the class are static, so it's not necessary to create an
* instance of the class in order to use it. However, creating an instance
* doesn't produce any more code and it may make the source code smaller and
* make it easier to switch to the `BeepPin2` class if it becomes necessary.
*
* The following is a basic example sketch, which will generate a tone when
* a button is pressed.
*
* \code
* #include <Arduboy2.h>
* // There's no need to #include <Arduboy2Beep.h>
* // It will be included in Arduboy2.h
*
* Arduboy2 arduboy;
* BeepPin1 beep; // class instance for speaker pin 1
*
* void setup() {
* arduboy.begin();
* arduboy.setFrameRate(50);
* beep.begin(); // set up the hardware for playing tones
* }
*
* void loop() {
* if (!arduboy.nextFrame()) {
* return;
* }
*
* beep.timer(); // handle tone duration
*
* arduboy.pollButtons();
*
* if (arduboy.justPressed(A_BUTTON)) {
* // play a 1000Hz tone for 100 frames (2 seconds at 50 FPS)
* // beep.freq(1000) is used to convert 1000Hz to the required count
* beep.tone(beep.freq(1000), 100);
* }
* }
* \endcode
*
* \note
* These functions, and the equivalents in class `BeepPin2`, will not work with
* a DevKit Arduboy because the speaker pins used cannot be directly controlled
* by a timer/counter. "Dummy" functions are provided so a sketch will compile
* and work properly but no sound will be produced.
*
* \see BeepPin2
*/
class BeepPin1
{
public:
/** \brief
* The counter used by the `timer()` function to time the duration of a tone.
*
* \details
* This variable is set by the `dur` parameter of the `tone()` function.
* It is then decremented each time the `timer()` function is called, if its
* value isn't 0. When `timer()` decrements it to 0, a tone that is playing
* will be stopped.
*
* A sketch can determine if a tone is currently playing by testing if
* this variable is non-zero (assuming it's a timed tone, not a continuous
* tone).
*
* Example:
* \code
* beep.tone(beep.freq(1000), 15);
* while (beep.duration != 0) { } // wait for the tone to stop playing
* \endcode
*
* It can also be manipulated directly by the sketch, although this should
* seldom be necessary.
*/
static uint8_t duration;
/** \brief
* Set up the hardware.
*
* \details
* Prepare the hardware for playing tones.
* This function must be called (usually in `setup()`) before using any of
* the other functions in this class.
*/
static void begin();
/** \brief
* Play a tone continually, until replaced by a new tone or stopped.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
*
* \details
* A tone is played indefinitely, until replaced by another tone or stopped
* using `noTone()`.
*
* The tone's frequency is determined by the specified count, which is loaded
* into the timer/counter that generates the tone. A desired frequency can be
* converted into the required count value using the `freq()` function.
*
* \see freq() timer() noTone()
*/
static void tone(uint16_t count);
/** \brief
* Play a tone for a given duration.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
* \param dur The duration of the tone, used by `timer()`.
*
* \details
* A tone is played for the specified duration, or until replaced by another
* tone or stopped using `noTone()`.
*
* The tone's frequency is determined by the specified count, which is loaded
* into the timer/counter that generates the tone. A desired frequency can be
* converted into the required count value using the `freq()` function.
*
* The duration value is the number of times the `timer()` function must be
* called before the tone is stopped.
*
* \see freq() timer() noTone()
*/
static void tone(uint16_t count, uint8_t dur);
/** \brief
* Handle the duration that a tone plays for.
*
* \details
* This function must be called at a constant interval, which would normally
* be once per frame, in order to stop a tone after the desired tone duration
* has elapsed.
*
* If the value of the `duration` variable is not 0, it will be decremented.
* When the `duration` variable is decremented to 0, a playing tone will be
* stopped.
*/
static void timer();
/** \brief
* Stop a tone that is playing.
*
* \details
* If a tone is playing it will be stopped. It's safe to call this function
* even if a tone isn't currently playing.
*
* \see tone()
*/
static void noTone();
/** \brief
* Convert a frequency to the required count.
*
* \param hz The frequency, in hertz (cycles per second), to be converted
* to a count.
*
* \return The required count to be loaded into the timer/counter for the
* given frequency.
*
* \details
* This helper function will convert a desired tone frequency to the closest
* value required by the `tone()` function's `count` parameter.
* The calculated count is rounded up or down to the nearest integer,
* if necessary.
*
* Example:
* \code
* beep.tone(beep.freq(440)); // play a 440Hz tone until stopped or replaced
* \endcode
*
* \note
* It is intended that `freq()` only be called with constant values.
* If `freq()` is called with a variable, code to perform floating point math
* will be included in the sketch, which will likely greatly increase the
* sketch's code size unless the sketch also uses floating point math for
* other purposes.
*/
static constexpr uint16_t freq(const float hz)
{
return (uint16_t) (((F_CPU / 8 / 2) + (hz / 2)) / hz) - 1;
}
};
/** \brief
* Play simple square wave tones using speaker pin 2.
*
* \details
* This class contains the same functions as class `BeepPin1` except they use
* speaker pin 2 instead of speaker pin 1.
*
* Using `BeepPin1` is more desirable, as it uses a 16 bit Timer, which can
* produce a greater frequency range and resolution than the 10 bit Timer
* used by `BeepPin2`. However, if the sketch also includes other sound
* generating code that uses speaker pin 1, `BeepPin2` can be used to avoid
* conflict.
*
* Tone frequencies on speaker pin 2 can range from 61.04Hz to 15625Hz using
* allowed counts from 3 to 1023.
*
* The formulas for frequency/count conversion are:
*
* count=(62500/frequency)-1
* frequency=62500/(count+1)
*
* See the documentation for `BeepPin1` for more details.
*
* \see BeepPin1
*/
class BeepPin2
{
public:
/** \brief
* The counter used by the `timer()` function to time the duration of a tone
* played on speaker pin 2.
*
* \details
* For details see `BeepPin1::duration`.
*/
static uint8_t duration;
/** \brief
* Set up the hardware for playing tones using speaker pin 2.
*
* \details
* For details see `BeepPin1::begin()`.
*/
static void begin();
/** \brief
* Play a tone on speaker pin 2 continually, until replaced by a new tone
* or stopped.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
*
* \details
* For details see `BeepPin1::tone(uint16_t)`.
*/
static void tone(uint16_t count);
/** \brief
* Play a tone on speaker pin 2 for a given duration.
*
* \param count The count to be loaded into the timer/counter to play
* the desired frequency.
* \param dur The duration of the tone, used by `timer()`.
*
* \details
* For details see `BeepPin1::tone(uint16_t, uint8_t)`.
*/
static void tone(uint16_t count, uint8_t dur);
/** \brief
* Handle the duration that a tone on speaker pin 2 plays for.
*
* \details
* For details see `BeepPin1::timer()`.
*/
static void timer();
/** \brief
* Stop a tone that is playing on speaker pin 2.
*
* \details
* For details see `BeepPin1::noTone()`.
*/
static void noTone();
/** \brief
* Convert a frequency to the required count for speaker pin 2.
*
* \param hz The frequency, in hertz (cycles per second), to be converted
* to a count.
*
* \return The required count to be loaded into the timer/counter for the
* given frequency.
*
* \details
* For details see `BeepPin1::freq()`.
*/
static constexpr uint16_t freq(const float hz)
{
return (uint16_t) (((F_CPU / 128 / 2) + (hz / 2)) / hz) - 1;
}
};
#endif