speaker: Add pwm functionality and 5 new examples to demonstrate

These changes add the ability to intialize a speaker instance to use a
PWM pin instead of a standard GPIO pin in order to emit sounds.

The current GPIO functionality is fairly constrained by the
implementation -- it only permits playing cerain hardcoded "notes".

The PWM support allows one to emit specific frequencies (currently
between 50-32Khz) using a PWM pin.  Of course, you may still be
constrained by the limits of your PWM hardware in the end.

There are a few new functions provided to support this PWM mode.  To
use the PWM mode of speaker, use the speaker_init_pwm() initialization
routine instead of speaker_init() (for C). For C++ and the SWIG
languages, pass "true" as the second argument to the constructor to
enable PWM mode.  The default is "false", to match current default
behavior (GPIO).

Functions that are not related to a given mode (GPIO vs. PWM) will do
nothing if not usable in that mode.

Signed-off-by: Jon Trulson <jtrulson@ics.com>
This commit is contained in:
Jon Trulson
2017-02-01 17:03:10 -07:00
parent 0749f130e1
commit dac31a0347
12 changed files with 563 additions and 11 deletions

View File

@ -62,7 +62,7 @@ static noteData_t note_list[7] = { // index, note
// forward decl
static void speaker_sound(const speaker_context dev, int note_delay);
speaker_context speaker_init(int pin)
static speaker_context _common_init()
{
// make sure MRAA is initialized
int mraa_rv;
@ -81,6 +81,17 @@ speaker_context speaker_init(int pin)
// zero out context
memset((void *)dev, 0, sizeof(struct _speaker_context));
return dev;
}
speaker_context speaker_init(int pin)
{
speaker_context dev = NULL;
if (!(dev = _common_init()))
return NULL;
dev->is_pwm = false;
if (!(dev->gpio = mraa_gpio_init(pin)))
{
printf("%s: mraa_gpio_init() failed.\n", __FUNCTION__);
@ -98,6 +109,28 @@ speaker_context speaker_init(int pin)
return dev;
}
// initialization of PWM uasage
speaker_context speaker_init_pwm(int pin)
{
speaker_context dev = NULL;
if (!(dev = _common_init()))
return NULL;
dev->is_pwm = true;
if (!(dev->pwm = mraa_pwm_init(pin)))
{
printf("%s: mraa_pwm_init() failed.\n", __FUNCTION__);
speaker_close(dev);
return NULL;
}
// set the default frequency to 1Khz
dev->default_freq = 1000;
return dev;
}
void speaker_close(speaker_context dev)
{
assert(dev != NULL);
@ -105,13 +138,114 @@ void speaker_close(speaker_context dev)
if (dev->gpio)
mraa_gpio_close(dev->gpio);
if (dev->pwm)
{
speaker_off(dev);
mraa_pwm_close(dev->pwm);
}
free(dev);
}
upm_result_t speaker_set_frequency(const speaker_context dev,
unsigned int freq)
{
assert(dev != NULL);
if (!dev->is_pwm)
return UPM_ERROR_NO_RESOURCES;
if (freq < 50 || freq > 32000)
{
printf("%s: freq must be between 50 and 32000.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
float period = 1.0 / (float)freq;
// printf("PERIOD: %f (freq %d)\n", period, freq);
if (period >= 0.001) // ms range
{
if (mraa_pwm_period(dev->pwm, period))
{
printf("%s: mraa_pwm_period() failed.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
}
else // us range. With a max of 32KHz, no less than 3.125 us.
{
if (mraa_pwm_period_us(dev->pwm, (int)(period * 1000000)))
{
printf("%s: mraa_pwm_period_us() failed.\n", __FUNCTION__);
return UPM_ERROR_OPERATION_FAILED;
}
}
// save it for later if needed
dev->default_freq = freq;
// A 10% duty cycle enables better results at high frequencies
mraa_pwm_write(dev->pwm, 0.1);
return UPM_SUCCESS;
}
upm_result_t speaker_emit(const speaker_context dev, unsigned int freq,
unsigned int emit_ms)
{
assert(dev != NULL);
if (!dev->is_pwm)
return UPM_ERROR_NO_RESOURCES;
if (speaker_set_frequency(dev, freq))
return UPM_ERROR_OPERATION_FAILED;
upm_clock_t clock;
upm_clock_init(&clock);
mraa_pwm_enable(dev->pwm, 1);
while (upm_elapsed_ms(&clock) < emit_ms)
; // loop until finished
mraa_pwm_enable(dev->pwm, 0);
return UPM_SUCCESS;
}
void speaker_on(const speaker_context dev)
{
assert(dev != NULL);
if (!dev->is_pwm)
return;
speaker_set_frequency(dev, dev->default_freq);
mraa_pwm_enable(dev->pwm, 1);
}
void speaker_off(const speaker_context dev)
{
assert(dev != NULL);
if (!dev->is_pwm)
return;
mraa_pwm_enable(dev->pwm, 0);
}
void speaker_play_all(const speaker_context dev)
{
assert(dev != NULL);
if (dev->is_pwm)
return;
speaker_play_sound(dev, 'c', false, "low");
upm_delay_us(200000);
speaker_play_sound(dev, 'd', false, "low");
@ -133,6 +267,9 @@ void speaker_play_sound(const speaker_context dev, char letter, bool sharp,
{
assert(dev != NULL);
if (dev->is_pwm)
return;
int index = 0;
switch (letter)
{

View File

@ -34,12 +34,17 @@
using namespace upm;
Speaker::Speaker(int pin) :
m_speaker(speaker_init(pin))
Speaker::Speaker(int pin, bool usePWM) :
m_speaker(nullptr)
{
if (!m_speaker)
throw std::runtime_error(std::string(__FUNCTION__) +
": speaker_init() failed.");
if (usePWM)
m_speaker = speaker_init_pwm(pin);
else
m_speaker = speaker_init(pin);
if (!m_speaker)
throw std::runtime_error(std::string(__FUNCTION__) +
": speaker_init()/speaker_init_pwm() failed.");
}
Speaker::~Speaker()
@ -57,3 +62,26 @@ void Speaker::playSound(char letter, bool sharp, std::string vocalWeight)
speaker_play_sound(m_speaker, letter, sharp, vocalWeight.c_str());
}
void Speaker::emit(unsigned int freq, unsigned int emit_ms)
{
if (speaker_emit(m_speaker, freq, emit_ms))
throw std::runtime_error(std::string(__FUNCTION__) +
": speaker_emit() failed.");
}
void Speaker::setFrequency(unsigned int freq)
{
if (speaker_set_frequency(m_speaker, freq))
throw std::runtime_error(std::string(__FUNCTION__) +
": speaker_set_frequency() failed.");
}
void Speaker::on()
{
speaker_on(m_speaker);
}
void Speaker::off()
{
speaker_off(m_speaker);
}

View File

@ -31,6 +31,8 @@
#include <unistd.h>
#include <mraa/gpio.h>
#include <mraa/pwm.h>
#include <upm.h>
#ifdef __cplusplus
@ -49,17 +51,39 @@ extern "C" {
* Device context
*/
typedef struct _speaker_context {
// gpio version
mraa_gpio_context gpio;
// pwm version
mraa_pwm_context pwm;
// are using the original GPIO mechanism or PWM?
bool is_pwm;
// default frequency, or last frequency specified
unsigned int default_freq;
} *speaker_context;
/**
* Speaker GPIO init
* Speaker GPIO init. In this mode, only the speaker_play_all()
* and speaker_play_sound() function will work. The other
* functions only support PWM mode (see speaker_init_pwm()).
*
* @param pin Digital pin to use
* @return Device context
*/
speaker_context speaker_init(int pin);
/**
* Speaker PWM init. In this mode a PWM pin is used to emit tones
* (within the capabilities of your PWM hardware).
* speaker_play_all() and speaker_play_sound() will not operate in
* this mode. The default frequency is set to 1KHz.
*
* @param pin Digital PWM capable pin to use
* @return Device context
*/
speaker_context speaker_init_pwm(int pin);
/**
* Speaker close function
*
@ -68,14 +92,16 @@ extern "C" {
void speaker_close(speaker_context dev);
/**
* Plays all alto notes (lowest notes)
* Plays all alto notes (lowest notes). This function only
* operates in GPIO mode.
*
* @param dev Device context
*/
void speaker_play_all(const speaker_context dev);
/**
* Plays a sound and a note whether it's sharp or not
* Plays a sound and a note whether it's sharp or not. This
* function only operates in GPIO mode.
*
* @param dev Device context
* @param letter Character name of the note
@ -88,6 +114,58 @@ extern "C" {
void speaker_play_sound(const speaker_context dev, char letter, bool sharp,
const char *vocal_weight);
/**
* Emit a specific frequency for a given period of time and
* return. This function only operates when in PWM mode (ie: the
* speaker context was initialized with speaker_init_pwm()). The
* frequency is limited to between 50-32000Hz. In addition, the
* allowable frequencies may be restricted further by the
* capabilities of your PWM hardware.
*
* @param dev Device context
* @param freq The frequency to emit. Must be between 50 and 32000Hz
* inclusive.
* @param emit_ms The number of milliseconds to emit the frequency.
* @return UPM result
*/
upm_result_t speaker_emit(const speaker_context dev, unsigned int freq,
unsigned int emit_ms);
/**
* Set a default frequency to be used with speaker_on() and
* speaker_off(). This function only operates when in PWM mode
* (ie: the speaker context was initialized with
* speaker_init_pwm()). The frequency is limited to between
* 50-32000Hz. In addition, the allowable frequencies may be
* restricted further by the capabilities of your PWM hardware.
*
* @param dev Device context
* @param freq The frequency to emit. Must be between 50 and 32000Hz
* inclusive.
* @return UPM result
*/
upm_result_t speaker_set_frequency(const speaker_context dev,
unsigned int freq);
/**
* Turn the speaker on, and emit the frequency last specified with
* speaker_set_frequency() or speaker_emit(). This function only
* operates when in PWM mode (ie: the speaker context was
* initialized with speaker_init_pwm()).
*
* @param dev Device context
*/
void speaker_on(const speaker_context dev);
/**
* Turn the speaker off. This function only operates when in PWM
* mode (ie: the speaker context was initialized with
* speaker_init_pwm()).
*
* @param dev Device context
*/
void speaker_off(const speaker_context dev);
#ifdef __cplusplus
}
#endif

View File

@ -55,6 +55,14 @@ namespace upm {
* This sensor can generate different tones and sounds depending on the
* frequency of the input signal.
*
* It can operate in one of two modes: GPIO (default) and PWM.
*
* Depending on which mode is selected, some methods may not be
* usable. In GPIO mode, the playAll() and playSound() methods
* are supported. In PWM mode, setFrequency(), emit(), on() and
* off() are supported. Calling a method not appropriate for the
* mode will have no effect.
*
* @image html speaker.jpg
* @snippet speaker.cxx Interesting
*/
@ -64,13 +72,16 @@ namespace upm {
* Speaker constructor
*
* @param pin Digital pin to use
* @param usePWM If true, PWM mode will be used, otherwise
* GPIO mode (default) is used.
*/
Speaker(int pin);
Speaker(int pin, bool usePWM=false);
/**
* Speaker destructor
*/
~Speaker();
virtual ~Speaker();
/**
* Plays all alto notes (lowest notes)
*
@ -89,6 +100,48 @@ namespace upm {
*/
void playSound(char letter, bool sharp, std::string vocalWeight);
/**
* Emit a specific frequency for a given period of time and
* return. This function only operates when in PWM mode. The
* frequency is limited to between 50-32000Hz. In addition,
* the allowable frequencies may be restricted further by the
* capabilities of your PWM hardware.
*
* @param freq The frequency to emit. Must be between 50 and 32000Hz
* inclusive.
* @param emit_ms The number of milliseconds to emit the frequency.
*/
void emit(unsigned int freq, unsigned int emit_ms);
/**
* Set a default frequency to be used with on() and off().
* This function only operates when in PWM mode.
* The frequency is limited to between 50-32000Hz. In
* addition, the allowable frequencies may be restricted
* further by the capabilities of your PWM hardware.
*
* @param freq The frequency to emit. Must be between 50 and 32000Hz
* inclusive.
*/
void setFrequency(unsigned int freq);
/**
* Turn the speaker on, and emit the frequency last specified
* with setFrequency() or emit(). This function only operates
* when in PWM mode.
*
* @param dev Device context
*/
void on();
/**
* Turn the speaker off. This function only operates when in
* PWM mode.
*
* @param dev Device context
*/
void off();
protected:
speaker_context m_speaker;