/* ---------------------------------------------------------------------------- * ATMEL Microcontroller Software Support * ---------------------------------------------------------------------------- * Copyright (c) 2008, Atmel Corporation * Copyright (c) 2011, Harald Welte * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the disclaimer below. * * Atmel's name may not be used to endorse or promote products derived from * this software without specific prior written permission. * * DISCLAIMER: 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 * 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. * ---------------------------------------------------------------------------- */ //----------------------------------------------------------------------------- /// \dir "USB audio speaker" /// /// !!!Purpose /// /// The USB Audio Speaker Project will help you to get familiar with the /// USB Device Port(UDP) and SSC interface on AT91SAM microcontrollers. Also /// it can help you to be familiar with the USB Framework that is used for /// rapid development of USB-compliant class drivers such as USB Audio Device /// class. /// /// You can find following information depends on your needs: /// - Sample usage of USB Audio Device Class driver and SSC-I2S driver. /// - USB Audio Class driver development based on the AT91 USB Framework. /// - USB enumerate sequence, the standard and class-specific descriptors and /// requests handling. /// - The initialize sequence and usage of UDP interface. /// - The initialize sequence and usage of SSC interface with PDC. /// /// !See /// - ssc: SSC interface driver /// - "i2s-wm8731: I2S codec wm8731 driver /// - usb: USB Framework, Audio Device Class driver and UDP interface driver /// - "AT91 USB device framework" /// - "USBD API" /// - "audio-speaker" /// /// !!!Requirements /// /// This package can be used with all Atmel evaluation kits that have both /// UDP and SSC interface. /// /// The current supported board list: /// - at91sam3u-ek /// /// !!!Description /// /// When an EK running this program connected to a host (PC for example), with /// USB cable, the EK appears as a desktop speaker for the host. Then the host /// can play sound through host software. The %audio stream from the host is /// then sent to the EK, and eventually sent to %audio DAC connected to AC97 /// of AT91SAM chips. At the same time, the %audio stream received is also sent /// back to host from EK for recording. /// /// !!!Usage /// /// -# Build the program and download it inside the evaluation board. Please /// refer to the /// /// SAM-BA User Guide, the /// /// GNU-Based Software Development application note or to the /// /// IAR EWARM User Guide, depending on your chosen solution. /// -# On the computer, open and configure a terminal application /// (e.g. HyperTerminal on Microsoft Windows) with these settings: /// - 115200 bauds /// - 8 bits of data /// - No parity /// - 1 stop bit /// - No flow control /// -# Start the application. /// -# In the terminal window, the following text should appear: /// \code /// -- USB Device Audio Speaker Project xxx -- /// -- AT91xxxxxx-xx /// -- Compiled: xxx xx xxxx xx:xx:xx -- /// \endcode /// -# When connecting USB cable to windows, the LED blinks, and the host /// reports a new USB %device attachment (if it's the first time you connect /// an %audio speaker demo board to your host). You can find new /// "USB Composite Device" and "USB Audio Device" appear in the hardware /// %device list. /// -# You can play sound in host side through the USB Audio Device, and it /// can be heard from the earphone connected to the EK. /// -# When playing sound, you can also record through the USB Audio Device on /// the host. /// //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- /// \unit /// /// !Purpose /// /// This file contains all the specific code for the /// usb-device-audio-speaker-project /// /// !Contents /// /// The code can be roughly broken down as follows: /// - Configuration functions /// - VBus_Configure /// - PIO & Clock configurations in start of main /// - Interrupt handlers /// - ISR_SSC /// - Callback functions /// - AUDDSpeakerChannel_MuteChanged /// - FrameReceived /// - The main function, which implements the program behavior /// /// Please refer to the list of functions in the #Overview# tab of this unit /// for more detailed information. //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- // Headers //------------------------------------------------------------------------------ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define AUDIO_USING_DMA #if defined(AUDIO_USING_DMA) #include #include #endif //------------------------------------------------------------------------------ // Definitions //------------------------------------------------------------------------------ /// Master clock frequency in Hz #define SSC_MCK 49152000 // TWI clock #define TWI_CLOCK 100000 /// Number of available audio buffers. #define BUFFER_NUMBER 5 /// Size of one buffer in bytes. #define BUFFER_SIZE (AUDDSpeakerDriver_BYTESPERFRAME + \ AUDDSpeakerDriver_BYTESPERSUBFRAME) /// Delay in ms for starting the DAC transmission after a frame has been received. #define DAC_DELAY 2 #define SAMPLE_RATE (48000) #define SLOT_BY_FRAME (2) #define BITS_BY_SLOT (16) #define AT91C_I2S_MASTER_TX_SETTING(nb_bit_by_slot, nb_slot_by_frame)( +\ AT91C_SSC_CKS_DIV +\ AT91C_SSC_CKO_CONTINOUS +\ AT91C_SSC_START_FALL_RF +\ ((1<<16) & AT91C_SSC_STTDLY) +\ ((((nb_bit_by_slot*nb_slot_by_frame)/2)-1) <<24)) #define AT91C_I2S_TX_FRAME_SETTING(nb_bit_by_slot, nb_slot_by_frame)( +\ (nb_bit_by_slot-1) +\ AT91C_SSC_MSBF +\ (((nb_slot_by_frame-1)<<8) & AT91C_SSC_DATNB) +\ (((nb_bit_by_slot-1)<<16) & AT91C_SSC_FSLEN) +\ AT91C_SSC_FSOS_NEGATIVE) // PMC define #define AT91C_CKGR_PLLR AT91C_CKGR_PLLAR #define AT91C_PMC_LOCK AT91C_PMC_LOCKA #define AT91C_CKGR_MUL_SHIFT 16 #define AT91C_CKGR_OUT_SHIFT 14 #define AT91C_CKGR_PLLCOUNT_SHIFT 8 #define AT91C_CKGR_DIV_SHIFT 0 /// Use for power management #define STATE_IDLE 0 /// The USB device is in suspend state #define STATE_SUSPEND 4 /// The USB device is in resume state #define STATE_RESUME 5 //------------------------------------------------------------------------------ // Internal variables //------------------------------------------------------------------------------ /// State of USB, for suspend and resume unsigned char USBState = STATE_IDLE; /// Data buffers for receiving audio frames from the USB host. static unsigned char buffers[BUFFER_NUMBER][BUFFER_SIZE]; /// Number of bytes stored in each data buffer. static volatile unsigned int bufferSizes[BUFFER_NUMBER]; /// Next buffer in which USB data can be stored. static volatile unsigned int inBufferIndex = 0; /// Next buffer which should be sent to the DAC. static volatile unsigned int outBufferIndex = 0; /// Number of buffers that can be sent to the DAC. static volatile unsigned int numBuffersToSend = 0; /// Current state of the DAC transmission. static volatile unsigned int isDacActive = 0; /// Number of buffers to wait for before the DAC starts to transmit data. static volatile unsigned int dacDelay; /// List of pins to configure for the application. static const Pin pins[] = {PINS_TWI0, PINS_SSC_CODEC, PIN_PCK0}; static Twid twid; //------------------------------------------------------------------------------ // VBus monitoring (optional) //------------------------------------------------------------------------------ #if defined(PIN_USB_VBUS) #define VBUS_CONFIGURE() VBus_Configure() /// VBus pin instance. static const Pin pinVbus = PIN_USB_VBUS; //------------------------------------------------------------------------------ /// Handles interrupts coming from PIO controllers. //------------------------------------------------------------------------------ static void ISR_Vbus(const Pin *pPin) { // Check current level on VBus if (PIO_Get(&pinVbus)) { TRACE_INFO("VBUS conn\n\r"); USBD_Connect(); } else { TRACE_INFO("VBUS discon\n\r"); USBD_Disconnect(); } } //------------------------------------------------------------------------------ /// Configures the VBus pin to trigger an interrupt when the level on that pin /// changes. //------------------------------------------------------------------------------ static void VBus_Configure( void ) { TRACE_INFO("VBus configuration\n\r"); // Configure PIO PIO_Configure(&pinVbus, 1); PIO_ConfigureIt(&pinVbus, ISR_Vbus); PIO_EnableIt(&pinVbus); // Check current level on VBus if (PIO_Get(&pinVbus)) { // if VBUS present, force the connect TRACE_INFO("VBUS conn\n\r"); USBD_Connect(); } else { TRACE_INFO("VBUS discon\n\r"); USBD_Disconnect(); } } #else #define VBUS_CONFIGURE() USBD_Connect() #endif //#if defined(PIN_USB_VBUS) #if defined (CP15_PRESENT) //------------------------------------------------------------------------------ /// Put the CPU in 32kHz, disable PLL, main oscillator /// Put voltage regulator in standby mode //------------------------------------------------------------------------------ void LowPowerMode(void) { PMC_CPUInIdleMode(); } //------------------------------------------------------------------------------ /// Put voltage regulator in normal mode /// Return the CPU to normal speed 48MHz, enable PLL, main oscillator //------------------------------------------------------------------------------ void NormalPowerMode(void) { } #elif defined(at91sam7a3) //------------------------------------------------------------------------------ /// Put the CPU in 32kHz, disable PLL, main oscillator //------------------------------------------------------------------------------ void LowPowerMode(void) { // MCK=48MHz to MCK=32kHz // MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=SLCK : then change prescaler AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // disable PLL AT91C_BASE_PMC->PMC_PLLR = 0; // Disable Main Oscillator AT91C_BASE_PMC->PMC_MOR = 0; PMC_DisableProcessorClock(); } //------------------------------------------------------------------------------ /// Return the CPU to normal speed 48MHz, enable PLL, main oscillator //------------------------------------------------------------------------------ void NormalPowerMode(void) { // MCK=32kHz to MCK=48MHz // enable Main Oscillator AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN )); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) ); // enable PLL@96MHz AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) | (AT91C_CKGR_PLLCOUNT & (28<<8)) | (AT91C_CKGR_MUL & (0x48<<16))); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) ); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; // MCK=SLCK/2 : change prescaler first AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=PLLCK/2 : then change source AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); } #elif defined (at91sam7se) //------------------------------------------------------------------------------ /// Put the CPU in 32kHz, disable PLL, main oscillator /// Put voltage regulator in standby mode //------------------------------------------------------------------------------ void LowPowerMode(void) { // MCK=48MHz to MCK=32kHz // MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=SLCK : then change prescaler AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // disable PLL AT91C_BASE_PMC->PMC_PLLR = 0; // Disable Main Oscillator AT91C_BASE_PMC->PMC_MOR = 0; // Voltage regulator in standby mode : Enable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR |= AT91C_VREG_PSTDBY; PMC_DisableProcessorClock(); } //------------------------------------------------------------------------------ /// Put voltage regulator in normal mode /// Return the CPU to normal speed 48MHz, enable PLL, main oscillator //------------------------------------------------------------------------------ void NormalPowerMode(void) { // Voltage regulator in normal mode : Disable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR &= ~AT91C_VREG_PSTDBY; // MCK=32kHz to MCK=48MHz // enable Main Oscillator AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN )); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) ); // enable PLL@96MHz AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) | (AT91C_CKGR_PLLCOUNT & (28<<8)) | (AT91C_CKGR_MUL & (0x48<<16))); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) ); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; // MCK=SLCK/2 : change prescaler first AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=PLLCK/2 : then change source AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); } #elif defined (at91sam7s) //------------------------------------------------------------------------------ /// Put the CPU in 32kHz, disable PLL, main oscillator /// Put voltage regulator in standby mode //------------------------------------------------------------------------------ void LowPowerMode(void) { // MCK=48MHz to MCK=32kHz // MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=SLCK : then change prescaler AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // disable PLL AT91C_BASE_PMC->PMC_PLLR = 0; // Disable Main Oscillator AT91C_BASE_PMC->PMC_MOR = 0; // Voltage regulator in standby mode : Enable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR |= AT91C_VREG_PSTDBY; PMC_DisableProcessorClock(); } //------------------------------------------------------------------------------ /// Put voltage regulator in normal mode /// Return the CPU to normal speed 48MHz, enable PLL, main oscillator //------------------------------------------------------------------------------ void NormalPowerMode(void) { // Voltage regulator in normal mode : Disable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR &= ~AT91C_VREG_PSTDBY; // MCK=32kHz to MCK=48MHz // enable Main Oscillator AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN )); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) ); // enable PLL@96MHz AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) | (AT91C_CKGR_PLLCOUNT & (28<<8)) | (AT91C_CKGR_MUL & (0x48<<16))); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) ); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; // MCK=SLCK/2 : change prescaler first AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=PLLCK/2 : then change source AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); } #elif defined (at91sam7x) || defined (at91sam7xc) //------------------------------------------------------------------------------ /// Put the CPU in 32kHz, disable PLL, main oscillator /// Put voltage regulator in standby mode //------------------------------------------------------------------------------ void LowPowerMode(void) { // MCK=48MHz to MCK=32kHz // MCK = SLCK/2 : change source first from 48 000 000 to 18. / 2 = 9M AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=SLCK : then change prescaler AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_CSS_SLOW_CLK; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // disable PLL AT91C_BASE_PMC->PMC_PLLR = 0; // Disable Main Oscillator AT91C_BASE_PMC->PMC_MOR = 0; // Voltage regulator in standby mode : Enable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR |= AT91C_VREG_PSTDBY; PMC_DisableProcessorClock(); } //------------------------------------------------------------------------------ /// Put voltage regulator in normal mode /// Return the CPU to normal speed 48MHz, enable PLL, main oscillator //------------------------------------------------------------------------------ void NormalPowerMode(void) { // Voltage regulator in normal mode : Disable VREG Low Power Mode AT91C_BASE_VREG->VREG_MR &= ~AT91C_VREG_PSTDBY; // MCK=32kHz to MCK=48MHz // enable Main Oscillator AT91C_BASE_PMC->PMC_MOR = (( (AT91C_CKGR_OSCOUNT & (0x06 <<8)) | AT91C_CKGR_MOSCEN )); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MOSCS ) ); // enable PLL@96MHz AT91C_BASE_PMC->PMC_PLLR = ((AT91C_CKGR_DIV & 0x0E) | (AT91C_CKGR_PLLCOUNT & (28<<8)) | (AT91C_CKGR_MUL & (0x48<<16))); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK ) ); while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; // MCK=SLCK/2 : change prescaler first AT91C_BASE_PMC->PMC_MCKR = AT91C_PMC_PRES_CLK_2; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); // MCK=PLLCK/2 : then change source AT91C_BASE_PMC->PMC_MCKR |= AT91C_PMC_CSS_PLL_CLK ; while( !( AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY ) ); } #else //------------------------------------------------------------------------------ /// Put the CPU in low power mode (for customer) //------------------------------------------------------------------------------ static void LowPowerMode(void) { } //------------------------------------------------------------------------------ /// Return the CPU to normal speed (for customer) //------------------------------------------------------------------------------ static void NormalPowerMode(void) { } #endif //------------------------------------------------------------------------------ // Callbacks re-implementation //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// Invoked when the USB device leaves the Suspended state. By default, /// configures the LEDs. //------------------------------------------------------------------------------ void USBDCallbacks_Resumed(void) { // Initialize LEDs LED_Configure(USBD_LEDPOWER); LED_Set(USBD_LEDPOWER); LED_Configure(USBD_LEDUSB); LED_Clear(USBD_LEDUSB); USBState = STATE_RESUME; } //------------------------------------------------------------------------------ /// Invoked when the USB device gets suspended. By default, turns off all LEDs. //------------------------------------------------------------------------------ void USBDCallbacks_Suspended(void) { // Turn off LEDs LED_Clear(USBD_LEDPOWER); LED_Clear(USBD_LEDUSB); USBState = STATE_SUSPEND; } //------------------------------------------------------------------------------ /// Invoked when an audio channel get muted or unmuted. Mutes/unmutes the /// channel at the DAC level. /// \param channel Pointer to an AUDDSpeakerChannel instance. /// \param muted Indicates the new mute status of the channel. //------------------------------------------------------------------------------ void AUDDSpeakerChannel_MuteChanged( AUDDSpeakerChannel *channel, unsigned char muted) { // Master channel if (AUDDSpeakerChannel_GetNumber(channel) == AUDDSpeakerDriver_MASTERCHANNEL) { if (muted) { TRACE_INFO("MuteMaster "); //AT73C213_SetMuteStatus(1, 1); } else { TRACE_INFO("UnmuteMaster "); //AT73C213_SetMuteStatus(0, 0); } } } //------------------------------------------------------------------------------ // Internal functions //------------------------------------------------------------------------------ #if 0 //------------------------------------------------------------------------------ /// Handles interrupts coming from the SSC. Sends remaining audio buffers /// or stops the DAC transmission. //------------------------------------------------------------------------------ static void ISR_SSC(void) { if ((AT91C_BASE_SSC0->SSC_SR & AT91C_SSC_TXBUFE) != 0) { // End of transmission TRACE_DEBUG("End "); SSC_DisableInterrupts(AT91C_BASE_SSC0, AT91C_SSC_TXBUFE | AT91C_SSC_ENDTX); AT91C_BASE_SSC0->SSC_PTCR = AT91C_PDC_TXTDIS; isDacActive = 0; } else if (numBuffersToSend > 0) { // Check the number of available buffers if (numBuffersToSend > DAC_DELAY) { // Strip one sample on all channels bufferSizes[outBufferIndex] -= AUDDSpeakerDriver_NUMCHANNELS; } else if (numBuffersToSend < DAC_DELAY) { // Copy the last sample on all channels memcpy(&buffers[outBufferIndex][bufferSizes[outBufferIndex]], &buffers[outBufferIndex][bufferSizes[outBufferIndex] - AUDDSpeakerDriver_BYTESPERSUBFRAME], AUDDSpeakerDriver_BYTESPERSUBFRAME); bufferSizes[outBufferIndex] += AUDDSpeakerDriver_NUMCHANNELS; } // Load next buffer SSC_WriteBuffer(AT91C_BASE_SSC0 buffers[outBufferIndex], bufferSizes[outBufferIndex]); outBufferIndex = (outBufferIndex + 1) % BUFFER_NUMBER; numBuffersToSend--; } else { SSC_DisableInterrupts(AT91C_BASE_SSC0, AT91C_SSC_ENDTX); } } #endif #if defined(AUDIO_USING_DMA) void HDMA_IrqHandler(void) { unsigned intFlag = 1 << (BOARD_SSC_DMA_CHANNEL + 8); if (numBuffersToSend > 0) { SSC_WriteBuffer(AT91C_BASE_SSC0, buffers[outBufferIndex], bufferSizes[outBufferIndex]); outBufferIndex = (outBufferIndex + 1) % BUFFER_NUMBER; DMA_EnableIt(intFlag); DMA_EnableChannel(BOARD_SSC_DMA_CHANNEL); numBuffersToSend--; } else { DMA_DisableIt(intFlag); DMA_DisableChannel(BOARD_SSC_DMA_CHANNEL); SSC_DisableTransmitter(AT91C_BASE_SSC0); } } #endif //------------------------------------------------------------------------------ /// Invoked when a frame has been received. //------------------------------------------------------------------------------ static void FrameReceived(unsigned int unused, char status, unsigned int transferred, unsigned int remaining) { bufferSizes[inBufferIndex] = transferred / AUDDSpeakerDriver_BYTESPERSAMPLE; inBufferIndex = (inBufferIndex + 1) % BUFFER_NUMBER; numBuffersToSend++; TRACE_DEBUG("FrameReceived "); // Start DAc transmission if necessary if (!isDacActive) { TRACE_DEBUG("Start "); dacDelay = DAC_DELAY; isDacActive = 1; } // Wait until a few buffers have been received else if (dacDelay > 0) { dacDelay--; } // Start sending buffers else { #if defined(AUDIO_USING_DMA) SSC_EnableTransmitter(AT91C_BASE_SSC0); unsigned intFlag = 1 << (BOARD_SSC_DMA_CHANNEL + 8); DMA_DisableIt(intFlag); DMA_DisableChannel(BOARD_SSC_DMA_CHANNEL); SSC_WriteBuffer(AT91C_BASE_SSC0, buffers[outBufferIndex], bufferSizes[outBufferIndex]); outBufferIndex = (outBufferIndex + 1) % BUFFER_NUMBER; numBuffersToSend--; #if 0 SSC_WriteBuffer(AT91C_BASE_SSC0, buffers[outBufferIndex], bufferSizes[outBufferIndex]); outBufferIndex = (outBufferIndex + 1) % BUFFER_NUMBER; numBuffersToSend--; #endif DMA_EnableIt(intFlag); DMA_EnableChannel(BOARD_SSC_DMA_CHANNEL); #else #error DMA required for audio!! #endif } // Receive next packet AUDDSpeakerDriver_Read(buffers[inBufferIndex], AUDDSpeakerDriver_BYTESPERFRAME, (TransferCallback) FrameReceived, 0); // No optional argument } //------------------------------------------------------------------------------ // Exported functions //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ /// Starts the driver and waits for an audio input stream to forward to the SSC. //------------------------------------------------------------------------------ int main(void) { TRACE_CONFIGURE(DBGU_STANDARD, 115200, BOARD_MCK); printf("-- USB Device Audio Speaker Project %s --\n\r", SOFTPACK_VERSION); printf("-- %s\n\r", BOARD_NAME); printf("-- Compiled: %s %s --\n\r", __DATE__, __TIME__); // Initialize all PIOs PIO_Configure(pins, PIO_LISTSIZE(pins)); // Initialize PSRAM BOARD_ConfigurePsram(); // Switch to Main clock AT91C_BASE_PMC->PMC_MCKR = (AT91C_BASE_PMC->PMC_MCKR & ~AT91C_PMC_CSS) | AT91C_PMC_CSS_MAIN_CLK; while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY) == 0); // Configure PLL to 98.285MHz *AT91C_CKGR_PLLR = ((1 << 29) | (171 << AT91C_CKGR_MUL_SHIFT) \ | (0x0 << AT91C_CKGR_OUT_SHIFT) |(0x3f << AT91C_CKGR_PLLCOUNT_SHIFT) \ | (21 << AT91C_CKGR_DIV_SHIFT)); while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_LOCK) == 0); // Configure master clock in two operations AT91C_BASE_PMC->PMC_MCKR = (( AT91C_PMC_PRES_CLK_2 | AT91C_PMC_CSS_PLLA_CLK) & ~AT91C_PMC_CSS) | AT91C_PMC_CSS_MAIN_CLK; while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY) == 0); AT91C_BASE_PMC->PMC_MCKR = ( AT91C_PMC_PRES_CLK_2 | AT91C_PMC_CSS_PLLA_CLK); while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_MCKRDY) == 0); // DBGU reconfiguration DBGU_Configure(DBGU_STANDARD, 115200, SSC_MCK); // Configure and enable the TWI (required for accessing the DAC) *AT91C_PMC_PCER = (1<< AT91C_ID_TWI0); TWI_ConfigureMaster(AT91C_BASE_TWI0, TWI_CLOCK, SSC_MCK); TWID_Initialize(&twid, AT91C_BASE_TWI0); // Enable the DAC master clock AT91C_BASE_PMC->PMC_PCKR[0] = AT91C_PMC_CSS_PLLA_CLK | AT91C_PMC_PRES_CLK_8; AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_PCK0; while ((AT91C_BASE_PMC->PMC_SR & AT91C_PMC_PCKRDY0) == 0); // Initialize the audio DAC WM8731_DAC_Init(&twid, WM8731_SLAVE_ADDRESS); // Configure SSC SSC_Configure(AT91C_BASE_SSC0, AT91C_ID_SSC0, SAMPLE_RATE * BITS_BY_SLOT * 2, SSC_MCK); SSC_ConfigureReceiver(AT91C_BASE_SSC0, 0, 0); SSC_ConfigureTransmitter(AT91C_BASE_SSC0, AT91C_I2S_MASTER_TX_SETTING(BITS_BY_SLOT, SLOT_BY_FRAME), AT91C_I2S_TX_FRAME_SETTING( BITS_BY_SLOT, SLOT_BY_FRAME)); SSC_DisableTransmitter(AT91C_BASE_SSC0); #if defined(AUDIO_USING_DMA) // Initialize DMA controller. DMAD_Initialize(BOARD_SSC_DMA_CHANNEL, 0); // Configure and enable the SSC interrupt IRQ_ConfigureIT(AT91C_ID_HDMA, 0, HDMA_IrqHandler); IRQ_EnableIT(AT91C_ID_HDMA); #endif // USB audio driver initialization AUDDSpeakerDriver_Initialize(); // connect if needed VBUS_CONFIGURE(); while (USBD_GetState() < USBD_STATE_CONFIGURED); // Infinite loop while (1) { if( USBState == STATE_SUSPEND ) { TRACE_DEBUG("suspend !\n\r"); LowPowerMode(); USBState = STATE_IDLE; } if( USBState == STATE_RESUME ) { // Return in normal MODE TRACE_DEBUG("resume !\n\r"); NormalPowerMode(); USBState = STATE_IDLE; } // Read the incoming audio stream AUDDSpeakerDriver_Read(buffers[inBufferIndex], AUDDSpeakerDriver_BYTESPERFRAME, (TransferCallback) FrameReceived, 0); // No optional argument } return 0; }