atmel-asf-projects/sam0/applications/dac_sound_player/dac_sound_player.c

266 lines
8.4 KiB
C

/**
* \file
*
* \brief SAM DAC Sound Player Application
*
* Copyright (C) 2013-2015 Atmel Corporation. All rights reserved.
*
* \license
* \asf_license_start
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*/
/**
* \mainpage SAM DAC Sound Player Application
* See \ref appdoc_main "here" for project documentation.
* \copydetails appdoc_preface
*
*
* \page appdoc_preface Overview
* This application demonstrates a simple sound player, sourcing a waveform
* from the device's Non-Volatile Memory and streaming it out of the DAC as
* an analog waveform.
*/
/**
* \page appdoc_main SAM DAC Sound Player Application
*
* Overview:
* - \ref appdoc_sam0_dac_sound_player_intro
* - \ref appdoc_sam0_dac_sound_player_setup
* - \ref appdoc_sam0_dac_sound_player_usage
* - \ref appdoc_sam0_dac_sound_player_compinfo
* - \ref appdoc_sam0_dac_sound_player_contactinfo
*
* \section appdoc_sam0_dac_sound_player_intro Introduction
* This application demonstrates a simple sound player, sourcing a waveform
* from the device's Non-Volatile Memory and streaming it out of the DAC as
* an analog waveform.
*
* A timer is used to guarantee the DAC conversions are performed at the correct
* sample rate, using the Event System module of the device to link the periodic
* timer output events to the DAC module to trigger new sample conversions.
*
* This application has been tested on following boards:
* - SAM D20/D21/L21/C21 Xplained Pro
*
* \section appdoc_sam0_dac_sound_player_setup Hardware Setup
* The device's DAC channel output should be connected to an audio amplifier,
* speaker, oscilloscope or other similar monitoring equipment so that the
* generated waveform can be monitored.
*
* \section appdoc_sam0_dac_sound_player_usage Usage
* On startup the device hardware will be configured, and the example will enter
* an infinite loop. Each time the board button is pressed, the embedded
* waveform will be output through the DAC and the board LED will be toggled
*
* \section appdoc_sam0_dac_sound_player_compinfo Compilation Info
* This software was written for the GNU GCC and IAR for ARM.
* Other compilers may or may not work.
*
* \section appdoc_sam0_dac_sound_player_contactinfo Contact Information
* For further information, visit
* <a href="http://www.atmel.com">http://www.atmel.com</a>.
*/
/*
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
*/
#include <asf.h>
/** Sample rate of the sound table. */
const uint32_t sample_rate = 16000;
/** Embedded waveform table of sound samples to output. */
const uint16_t wav_samples[] = {
#include "data.x"
};
/** Number of samples in the waveform. */
const uint32_t number_of_samples =
(sizeof(wav_samples) / sizeof(wav_samples[0]));
/**
* \brief Configures the DAC in event triggered mode.
*
* Configures the DAC to use the module's default configuration, with output
* channel mode configured for event triggered conversions.
*
* \param dev_inst Pointer to the DAC module software instance to initialize
*/
static void configure_dac(struct dac_module *dac_module)
{
struct dac_config config;
struct dac_chan_config channel_config;
/* Get the DAC default configuration */
dac_get_config_defaults(&config);
/* Switch to GCLK generator 0 */
config.clock_source = GCLK_GENERATOR_0;
dac_init(dac_module, DAC, &config);
/* Get the default DAC channel config */
dac_chan_get_config_defaults(&channel_config);
#if (SAML21)
/* Set the channel configuration, and enable it */
dac_chan_set_config(dac_module, DAC_CHANNEL_1, &channel_config);
dac_chan_enable(dac_module, DAC_CHANNEL_1);
/* Enable event triggered conversions */
struct dac_events events = { .on_event_chan1_start_conversion = true };
#else
/* Set the channel configuration, and enable it */
dac_chan_set_config(dac_module, DAC_CHANNEL_0, &channel_config);
dac_chan_enable(dac_module, DAC_CHANNEL_0);
/* Enable event triggered conversions */
struct dac_events events = { .on_event_start_conversion = true };
#endif
dac_enable_events(dac_module, &events);
dac_enable(dac_module);
}
/**
* \brief Configures the TC to generate output events at the sample frequency.
*
* Configures the TC in Frequency Generation mode, with an event output once
* each time the audio sample frequency period expires.
*
* \param dev_inst Pointer to the TC module software instance to initialize
*/
static void configure_tc(struct tc_module *tc_module)
{
struct tc_config config;
tc_get_config_defaults(&config);
config.clock_source = GCLK_GENERATOR_0;
config.wave_generation = TC_WAVE_GENERATION_MATCH_FREQ;
tc_init(tc_module, TC3, &config);
/* Enable periodic event output generation */
struct tc_events events = { .generate_event_on_overflow = true };
tc_enable_events(tc_module, &events);
/* Set the timer top value to alter the overflow frequency */
tc_set_top_value(tc_module,
system_gclk_gen_get_hz(GCLK_GENERATOR_0) / sample_rate);
tc_enable(tc_module);
}
/**
* \brief Configures the event system to link the sample timer to the DAC.
*
* Configures the event system, linking the TC module used for the audio sample
* rate timing to the DAC, so that a new conversion is triggered each time the
* DAC receives an event from the timer.
*/
static void configure_events(struct events_resource *event)
{
struct events_config config;
events_get_config_defaults(&config);
config.generator = EVSYS_ID_GEN_TC3_OVF;
config.path = EVENTS_PATH_ASYNCHRONOUS;
events_allocate(event, &config);
#if (SAML21)
events_attach_user(event, EVSYS_ID_USER_DAC_START_1);
#else
events_attach_user(event, EVSYS_ID_USER_DAC_START);
#endif
}
/**
* \brief Main application routine
*/
int main(void)
{
struct dac_module dac_module;
struct tc_module tc_module;
struct events_resource event;
/* Initialize all the system clocks, pm, gclk... */
system_init();
/* Enable the internal bandgap to use as reference to the DAC */
#if (SAML21) || (SAMC21)
system_voltage_reference_enable(SYSTEM_VOLTAGE_REFERENCE_OUTPUT);
#else
system_voltage_reference_enable(SYSTEM_VOLTAGE_REFERENCE_BANDGAP);
#endif
/* Module configuration */
configure_tc(&tc_module);
configure_dac(&dac_module);
configure_events(&event);
/* Start the sample trigger timer */
tc_start_counter(&tc_module);
while (true) {
while (port_pin_get_input_level(SW0_PIN) == SW0_INACTIVE) {
/* Wait for the button to be pressed */
}
port_pin_toggle_output_level(LED0_PIN);
for (uint32_t i = 0; i < number_of_samples; i++) {
#if (SAML21)
dac_chan_write(&dac_module, DAC_CHANNEL_1, wav_samples[i] << 2);
while (!dac_chan_is_end_of_conversion(&dac_module, DAC_CHANNEL_1)) {
#else
dac_chan_write(&dac_module, DAC_CHANNEL_0, wav_samples[i]);
while (!(DAC->INTFLAG.reg & DAC_INTFLAG_EMPTY)) {
#endif
/* Wait for data buffer to be empty */
}
}
while (port_pin_get_input_level(SW0_PIN) == SW0_ACTIVE) {
/* Wait for the button to be depressed */
}
}
}