A speaker is a device that produces sounds from the electrical signal having audio encoded. The speakers usually have a 3.5 mm jack for audio output from the computer. Nowadays USB interface is also gaining popularity for interfacing audio devices with the computer. A lot of portable audio and musical devices nowadays have USB connector for interfacing with the computers. The USB pluggable portable devices are easy to carry away along with a laptop or notebook computer anywhere. Plus, USB is now such a universal interface that it is more common than any other computer interface for serial communication. The USB pluggable devices also have the advantage that they can draw power from the USB port itself.
When USB interface is used by an audio device for connecting with the computer, the audio stream coming from the device also needs to be according to the USB protocol. In this project, a typical 3.5 mm audio jack of a regular speaker is replaced with the USB interface to connect with a computer. In order to decode audio stream from the computer according to USB protocol, there needs a controller chip that could accept the digital audio signal (square waveform with audio encoded in it) and decode it according to the USB protocol and convert it to the analog electrical signal. As a controller chip 8-bit USB AVR – Atmega 32u4 is used in the project. The AVR based Lightweight USB Framework (LUFA) is used as the firmware of the chip which is modified to decode digital audio output and send out the audio stream in the form of analog electrical signal.
Fig. 1: Prototype of Arduino based USB Speaker
In the USB protocol, there is a separate device class for audio devices – Audio Class 1.0. The open-source LUFA framework has the drivers for audio class devices as well. With the use of LUFA firmware, the device driver code to implement USB protocol is not needed to be written explicitly. Modifying the firmware code to customize the functioning of the Audio Class driver will be all sufficient.
The project uses Arduino Pro Micro as the controller board which connects to a personal computer by on-board USB port and USB cable. The board sends out the analog audio signal from an analog pin connecting to Analog In wire of the speaker.
PREREQUISITES
This project is based on Arduino Pro Micro which has the USB AVR – Atmega 32u4 as the sitting MCU. In order to understand this project, one must have basic knowledge of the AVR microcontrollers and the embedded C programming for AVRs. WinAVR Studio is used to write, edit and compile the project code, so closely following the project shall require familiarizing with the above stated IDE as well. Though LUFA framework takes care of implementing the USB protocol and has APIs to abstract the lower level codes, understanding USB protocol is recommended to understand how actually the project is working. In fact, if anyone has already worked on some other microcontroller, it will not be much pain to understand and follow this project as the project code is about getting digital audio signal at the analog pin of the AVR microcontroller and modifying the LUFA device driver for audio class to customize the device functioning.
Fig. 2: Image of Arduino based USB Speaker
COMPONENTS REQUIRED
1. Arduino Pro Micro
2. Breadboard
3. Connecting wires
4. Micro USB cable
5. Any regular speaker
SOFTWARE TOOLS REQUIRED
1. WinAVR Studio
2. AVR Dude
BLOCK DIAGRAM
Fig. 3: Block Diagram of Arduino based DIY USB Speakers
CIRCUIT CONNECTIONS
The project is built on Arduino Pro Micro and uses Atmega 32u4 as the USB controller chip. The Audio jack of the speaker has three wires – VCC, Ground and Analog In. The VCC and Ground wires are connected with the common VCC and ground of the circuit respectively. The Audio In wire of the speaker is connected with the D5 analog pin (Pin 6 of Port C on Arduino Pro Micro). A speaker with in-built amplifier should be used in the project. The board is connected to the computer from using USB cable. The program code for the project is burnt to the Arduino Pro Micro using AVR Dude.
HOW THE PROJECT WORKS
In this project the USB protocol is implemented by the LUFA framework. For configuring the controller chip to work as USB speaker, the Audio Class Driver of the LUFA framework will be used. The Audio Class 1.0 driver takes care of the transfers between the host device and the audio devices like microphone, speaker and USB Connect-able musical instruments.
When a USB device is attached to the host (PC), the host sends request for configuration details in the form of control transfer. The connected device has to respond with appropriate descriptors to get configured and ready for further operations. Only after configuration, the device can communicate with the host in the form of interrupt, isochronous or bulk transfers for executing the operations for which the device has been made. In case of speaker, after configuring with the host device, it has to communicate with the host in the form of isochronous transfers as audio data is a kind of time sensitive information. The process of identification and configuration of the device with the host is called enumeration.
Fig. 4: Image of Arduino based DIY USB Speakers
From the computer, device receives digital audio data which is converted to analog audio signal using Pulse Width Modulation (PWM). That is why, the Analog In pin of the speaker is connected to Analog Out or PWM pin of the microcontroller. The Host computer will sample the Audio Signal with a sampling frequency of 48 KHz. The sampling is needed to convert Analog Audio Data to Digital Data. After conversion, it will send the samples or Digital Data to the device. The device will collect the digital data and will generate PWM signal.
The sampling frequency of 48 KHz means one sample after every 20.83 microseconds. To collect these samples at an interval of 20.83 microseconds, the device will use inbuilt Timer 0 to track the time. The Timer 0 is used in CTC (Clear Timer on Compare) mode and the data is collected inside the compare interrupt. The Timer 3 is used to generate PWM using Output Compare mode. The output is taken at the PC6 pin of the Atmega32u4.
The speaker is an audio class USB device and LUFA framework has Audio class related modules in the LUFA-Source-Folder /LUFA/Drivers/USB/Class/Device folder. Other device class related module are also in the same folder. The LUFA framework has demo projects for different USB device classes in the LUFA-Source-FolderDemosDeviceClassDriver folder. For implementing the project, demo project for audio output provided in the LUFA framework will be modified and complied. The demo project for audio output is in the LUFA-Source-FolderDemosDeviceClassDriverAudioOutput folder. The folder contains AudioOutput.c file which will be modified to implement the project.
How AudioOutput.c identifies a device as speaker
The AudioOutput.c uses Speaker_Audio_Interface interface in Audio_Device_USBTask() function which is being imported from the AudioClassDevice.c (from LUFA-Source-Folder LUFADriversUSBClassDevice) to configure the device as speaker. The interface abstracts the low-level descriptor codes and identifies the device as speaker through ControlInterfaceNumber and StreamingInterfaceNumber variables.
Fig. 5: Image of Arduino based DIY USB Speakers
Speaker Specific Report Descriptors
Similar to any HID device, the audio class device also has to exchange data with the host which should be structured in the form of reports. The report descriptor defines the report structure. A report descriptor contains the information needed by the host to determine the data format and how the data should be processed by the host. Therefore, a report descriptor basically structures the data that needs to be exchanged with the host according to the USB protocol.
For working like a speaker, the audio device also needs to respond with proper descriptors in response to the requests made by the host. An audio class device has the following descriptors associated with it.
1) Device Descriptor
2) Configuration Descriptor
3) Audio Control (AC) Interface Descriptors
4) Audio Control (AC) Endpoint descriptors
5) Audio Streaming Interface Descriptors
6) Audio Streaming Endpoint Descriptors
The device descriptor and configuration descriptor for the audio output device are defined in the descriptor.h file in the same folder with definitions in the descriptor.c file in the same folder. The Audio Control (AC) interface, Audio Stream (AS) interface and Audio Stream (AS) Endpoint descriptors are defined for the audio output device and are defined within the configuration descriptor. The descriptors get the usage report item values from the AudioClassCommon.h (file located in LUFA-Source-FolderLUFADriversUSBClassCommon folder). The AudioOutput.c imports AudioOutput.h which imports USB.h. USB.h imports AudioCLass.h. In AudioClass.h is imported AudioClassDevice.h if the USB_CAN_BE_DEVICE is true for the controller chip to being a USB device not the host. The AudioClassDevice.h imports AudioClassCommon.h where the Audio Class device specific descriptor fields have been defined.
The Audio device class can respond to the following Audio Class requests defined in AudioClassCommon.h.
Fig. 6: Screenshot of Audio Class requests defined in AudioClassCommon.h
The sub class within Audio Class device are defined in the following manner.
Fig. 7: Screenshot of Sub class within Audio Class device
The Audio Control subtypes are defined in the following manner.
Fig. 8: Screenshot of Audio Control subtypes
The Audio Streaming subtypes are defined in the following manner.
Fig. 9: Screenshot of Audio Streaming subtypes
The generic AC interface descriptor is defined in the following manner.
Fig. 10: Screenshot of generic AC interface descriptor
The Audio Class specific AC interface descriptor is defined in the following manner.
Fig. 11: Screenshot of Audio Class specific AC interface descriptor
The generic Audio Stream Interface descriptor is defined in the following manner.
Fig. 12: Screenshot of generic Audio Stream Interface descriptor
The Audio Class specific Stream Interface descriptor is defined in the following manner.
Fig. 13: Screenshot of Audio Class specific Stream Interface descriptor
The generic Audio Stream Endpoint descriptor is defined in the following manner.
Fig. 14: Screenshot of generic Audio Stream Endpoint descriptor
The Audio Class specific Stream Endpoint descriptor is defined in the following manner.
Fig. 15: Screenshot of Audio Class specific Stream Endpoint descriptor
The report item values defined for the above-stated descriptors are taken from the “USB Device Class Definitions for Audio devices” provided by the USB Implementers Forum. The Host can access these report descriptors by requesting the device using GET_REPORT request. This report is transmitted using Control Transfer Type of the USB protocol.
HOW THE DEVICE WORKS
A regular speaker with an inbuilt amplifier will be interfaced to the microcontroller at its Analog Output pin. The microcontroller will collect the digital audio data from the computer and will generate an appropriate analog pulse width modulated signal at the PC6 pin.
To sample the digital audio data at regular intervals, the internal timer of Atmega32u4 will be used. The most commonly used sampling frequencies for audio conversion are 44.1 KHz, 48 KHz, 88.2 KHz, or 96 KHz. A 48 KHz sampling frequency will be used. This simply means one sample after every 20.83 microseconds.
The timer will be used in CTC (Clear Timer on Compare) mode and inside the compare interrupt, the audio signal will be sampled and the digital data will be sent to the host. In the CTC mode, there are two values, one is SET value and another one is the process value. Once the process value becomes equal (or more than) the SET value, the process value is reset.
The demo code for Audio Output given in the LUFA framework will be used to more or less extent as it is. Some minor modifications like commenting out unused features and defining the analog Output pin in AppConfig.h file will be needed to be done.
PROGRAMMING GUIDE
For building the project download the LUFA framework from the github.com. The demo project provided with the LUFA framework is modified to make the project. In the extracted LUFA zip file, open Demos/Device/ClassDriver/AudioOutput folder. The folder has the following files and folders.
Fig. 16: Screenshot of LUFA Library Folder on Windows
Of these, AudioOutput.h, AudioOutput.c, Appconfig.h (inside Config folder) and Makefile needs to be modified for this project. The modified files (provided at the bottom of the article in zip format) can also be downloaded from the engineersgarage and replaced with the original files. Either open the files in WinAVR Studio or Notepad++ and modify original files or replace files with the already modified one. The modified or replaced AudioOutput.c needs to be compiled from within the LUFA’s Source folder to get the object code.
Modifying AudioOutput.h
The AudioOutput.h library file is imported in the AudioOutput.c file and includes a set of additional libraries and defines the constants and functions needed for the device operation. These include the additional libraries for the joystick, button, and LEDs which should be commented out as the project is not using these features. So open AudioOutput.h and make the following changes -:
Save the file with changes.
Modifying AudioOutput.C file
Again in the AudioOutput.c, the code sections for Joystick, button board and LEDs need to be commented out. So open AudioOutput.c and make the following changes – :
• In the main loop, comment the LEDs_SetAllLEDs()
• In SetupHardware() function, comment the Joystick_Init(), LEDs_Init(), Buttons_Init()
• In EVENT_USB_Device_Connect() function, comment the LEDs_SetAllLEDs()
• In EVENT_USB_Device_Disconnect() function, comment LEDs_SetAllLEDs()
• In EVENT_USB_Device_ConfigurationChanged() function, comment the LEDs_SetAllLEDs()
In AudioOutput.c the main() function executes the functioning of the device. Inside the main function only the statement for LED board has to be commented out. The main function will have the following body.
Inside the infinite for loop, the Audio_Device_USBTask() function is called where Speaker_Audio_Interface interface is passed as parameter. The interface identifies the device as speaker and abstracts the low level program code specific to Audio Class Device. The function is coming from the AudioClassDevice.c module (located in LUFA/Drivers/USB/Class/Device/ folder) and is used for general management task for a given audio class interface, required for the correct operation of the interface. It should be called in the main program loop, before the master USB management task USB_USBTask(). The USB_USBTask() is the main USB management task. The USB driver requires this task to be executed continuously when the USB system is active (device attached in host mode, or attached to a host in device mode) in order to manage USB communications. The function is defined in USBTask.c (Located in LUFA-Source-FolderLUFADriversUSBCore folder).
Inside the EVENT_USB_Device_Connect() function, the Timer0 and Timer3 gets initialized and set to CTC mode and Output Compare mode respectively. When the USB device is plugged, this function gets executed. The EVENT_USB_Device_Connect() function has the following body – :
In the body of the function, the timer 0 is selected by setting the OCIE0A bit of the timer mask register. In this way, the Timer/Counter0 Compare Match A interrupt is enabled. The corresponding interrupt is executed. if a Compare Match in Timer/Counter0 occurs, i.e., when the OCF0A bit is set in the Timer/Counter 0 Interrupt Flag Register – TIFR0. The value in Output Compare Register A is set according to the CPU frequency and sampling rate by passing a value in OCR0A register. By setting the WGM01 bit in the Timer Control register A (TCCR0A), the CTC mode is selected. Finally, by setting the CS01 bit in the Timer Control Register B (TCCR0B), the clock source is set to 1/8 of the CPU clock.
The audio output could be in mono mode, stereo mode or parallel mode. By the circuit configuration, if a mono mode is set, the analog output will be received from the PC6 pin of the Arduino board. If the stereo mode is set, the analog output will be received from PC6 and PC5 pins of the Arduino board. In case, [parallel mode is set, the analog output will be received from the Port of the Arduino board. In the project, the only mono mode will be enabled and used.
For PWM output, the timer 3 is used. In the timer 3 control register A, by setting the WGM30 alone, phase corrected 8-bit PWM is selected. By setting COM3A1 and COM3A0, OC3A compare match for channel A is set which should set on up-counting and clear on down counting for phase correct PWM output. The similar is done with compare match for channel B by setting COM3B1 and COM3B0 of the timer control register A. In timer 3 control register B, by setting WGM32, Fast 8-bit PWM is selected and by setting CS30, baud rate is set to CPU speed.
The timer stops when the device is unplugged from the host. The timer stop statement can be found in EVENT_USB_Device_Disconnect() function. The EVENT_USB_Device_Disconnect() function has the following body.
In the body of the function, the timer control register B (responsible for setting clock source) for both timer 0 and timer 3 are cleared to stop the timers. These timer settings are important to set the sampling rate of the audio stream.
The most important function is the ISR (Interrupt Service Routine) for the Timer0 compare interrupt. In this function, the sampled data is collected from Host and PWM is generated according to the digital sample data collected. The Timer0 compare interrupt ISR is defined with name ISR (TIMER0_COMPA_vect, ISR_BLOCK). There are some statements inside this function that are not required and can be removed. They are -:
So the interrupt service routine will have the following body.
The interrupt service routine samples 16-bit audio data at every execution of the interrupt routine. The data is loaded as 8-bit samples on Channel A and B.
Modifying AppConfig.h file
The type of Audio output like Stereo mode, Mono mode or Parallel Digital Data can be set inside the file AppConfig.h. This file is located inside the Config folder. Inside this file, comment the statement #define AUDIO_OUT_STEREO and uncomment the statement #define AUDIO_OUT_MONO. The project makes use of Mono mode for output at pin5 (PC6) of the Arduino Pro Micro. Also, comment the statement #defines AUDIO_OUT_PORTC as the project will not use parallel data output mode.
Save the file and create Make file for the project.
Modifying Make File
In the AudioOutput folder, there is a make file that needs to be edited. The file can be edited using Notepad++. The following information needs to be edited
• MCU = atmega32u4
• ARCH = AVR8
• BOARD = LEONARDO
• F_CPU = 16000000
Save the file and exit. Now all the files are edited completely for the basic USB speaker project.
Compiling AudioOutput.c
For compiling the source code, WinAVR Programmers Notepad or Arduino IDE can be used. Open the modified AudioOutput.c file and compile the code.
BURNING HEX CODE
The hex file is generated on compiling the AudioOutput.c file. For burning the object code to microcontroller open the Command Prompt, change the current directory to the directory containing the Hex file. This can be done using command: CD <address of the directory>. Now reset the Arduino and instantly run the command: : avrdude -v -p atmega32u4 -c avr109 -P COM20 -b 57600 -D -Uflash:w:AudioOutput.hex:i after replacing the COM Port with the recognized one.
If the uploading process is successful, the Arduino will be shown as Audio Out Demo under the Sounds tab in the Device Manager. There is no need of installing any driver in the computer as Generic Audio Class device driver is used for the project implementation.
TESTING
For testing, connect the input of the amplifier to the pin5 (PC6) pin. Power the amplifier, and connect the device to the PC. Play any music file to test the device.
Any 3.5 mm jack can be used to connect speakers with the device. The signal pin of female jack can be connected at PC6 and the common pin at ground. Speakers having 3.5 mm male pin for connection can be plugged into the female jack.
In the next project – Atmega 32u4 Based USB Musical Keyword, learn how to make a USB pluggable Musical Keyboard also based on Audio Class Device of the USB standard.
Project Source Code
###
/* LUFA Library Copyright (C) Dean Camera, 2015. dean [at] fourwalledcubicle [dot] com www.lufa-lib.org */ /* Copyright 2015 Dean Camera (dean [at] fourwalledcubicle [dot] com) Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that the copyright notice and this permission notice and warranty disclaimer appear in supporting documentation, and that the name of the author not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The author disclaims all warranties with regard to this software, including all implied warranties of merchantability and fitness. In no event shall the author be liable for any special, indirect or consequential damages or any damages whatsoever resulting from loss of use, data or profits, whether in an action of contract, negligence or other tortious action, arising out of or in connection with the use or performance of this software. */ /** file * * Main source file for the AudioOutput demo. This file contains the main tasks of * the demo and is responsible for the initial application hardware configuration. */ #include "AudioOutput.h" /** LUFA Audio Class driver interface configuration and state information. This structure is * passed to all Audio Class driver functions, so that multiple instances of the same class * within a device can be differentiated from one another. */ USB_ClassInfo_Audio_Device_t Speaker_Audio_Interface = { .Config = { .ControlInterfaceNumber = INTERFACE_ID_AudioControl, .StreamingInterfaceNumber = INTERFACE_ID_AudioStream, .DataOUTEndpoint = { .Address = AUDIO_STREAM_EPADDR, .Size = AUDIO_STREAM_EPSIZE, .Banks = 2, }, }, }; /** Current audio sampling frequency of the streaming audio endpoint. */ static uint32_t CurrentAudioSampleFrequency = 48000; /** Main program entry point. This routine contains the overall program flow, including initial * setup of all components and the main program loop. */ int main(void) { SetupHardware(); //LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); GlobalInterruptEnable(); for (;;) { Audio_Device_USBTask(&Speaker_Audio_Interface); USB_USBTask(); } } /** Configures the board hardware and chip peripherals for the demo's functionality. */ void SetupHardware(void) { #if (ARCH == ARCH_AVR8) /* Disable watchdog if enabled by bootloader/fuses */ MCUSR &= ~(1 << WDRF); wdt_disable(); /* Disable clock division */ clock_prescale_set(clock_div_1); #endif /* Hardware Initialization */ //LEDs_Init(); USB_Init(); } /** ISR to handle the reloading of the PWM timer with the next sample. */ ISR(TIMER0_COMPA_vect, ISR_BLOCK) { uint8_t PrevEndpoint = Endpoint_GetCurrentEndpoint(); /* Check that the USB bus is ready for the next sample to read */ if (Audio_Device_IsSampleReceived(&Speaker_Audio_Interface)) { /* Retrieve the signed 16-bit left and right audio samples, convert to 8-bit */ int8_t LeftSample_8Bit = (Audio_Device_ReadSample16(&Speaker_Audio_Interface) >> 8); int8_t RightSample_8Bit = (Audio_Device_ReadSample16(&Speaker_Audio_Interface) >> 8); /* Mix the two channels together to produce a mono, 8-bit sample */ int8_t MixedSample_8Bit = (((int16_t)LeftSample_8Bit + (int16_t)RightSample_8Bit) >> 1); #if defined(AUDIO_OUT_MONO) /* Load the sample into the PWM timer channel */ OCR3A = (MixedSample_8Bit ^ (1 << 7)); #elif defined(AUDIO_OUT_STEREO) /* Load the dual 8-bit samples into the PWM timer channels */ OCR3A = (LeftSample_8Bit ^ (1 << 7)); OCR3B = (RightSample_8Bit ^ (1 << 7)); #elif defined(AUDIO_OUT_PORTC) /* Load the 8-bit mixed sample into PORTC */ PORTC = MixedSample_8Bit; #endif } Endpoint_SelectEndpoint(PrevEndpoint); } /** Event handler for the library USB Connection event. */ void EVENT_USB_Device_Connect(void) { //LEDs_SetAllLEDs(LEDMASK_USB_ENUMERATING); /* Sample reload timer initialization */ TIMSK0 = (1 << OCIE0A); OCR0A = ((F_CPU / 8 / CurrentAudioSampleFrequency) - 1); TCCR0A = (1 << WGM01); // CTC mode TCCR0B = (1 << CS01); // Fcpu/8 speed #if defined(AUDIO_OUT_MONO) /* Set speaker as output */ DDRC |= (1 << 6); #elif defined(AUDIO_OUT_STEREO) /* Set speakers as outputs */ DDRC |= ((1 << 6) | (1 << 5)); #elif defined(AUDIO_OUT_PORTC) /* Set PORTC as outputs */ DDRC |= 0xFF; #endif #if (defined(AUDIO_OUT_MONO) || defined(AUDIO_OUT_STEREO)) /* PWM speaker timer initialization */ TCCR3A = ((1 << WGM30) | (1 << COM3A1) | (1 << COM3A0) | (1 << COM3B1) | (1 << COM3B0)); // Set on match, clear on TOP TCCR3B = ((1 << WGM32) | (1 << CS30)); // Fast 8-Bit PWM, F_CPU speed #endif } /** Event handler for the library USB Disconnection event. */ void EVENT_USB_Device_Disconnect(void) { //LEDs_SetAllLEDs(LEDMASK_USB_NOTREADY); /* Stop the sample reload timer */ TCCR0B = 0; #if (defined(AUDIO_OUT_MONO) || defined(AUDIO_OUT_STEREO)) /* Stop the PWM generation timer */ TCCR3B = 0; #endif #if defined(AUDIO_OUT_MONO) /* Set speaker as input to reduce current draw */ DDRC &= ~(1 << 6); #elif defined(AUDIO_OUT_STEREO) /* Set speakers as inputs to reduce current draw */ DDRC &= ~((1 << 6) | (1 << 5)); #elif defined(AUDIO_OUT_PORTC) /* Set PORTC low */ PORTC = 0x00; #endif } /** Event handler for the library USB Configuration Changed event. */ void EVENT_USB_Device_ConfigurationChanged(void) { bool ConfigSuccess = true; ConfigSuccess &= Audio_Device_ConfigureEndpoints(&Speaker_Audio_Interface); //LEDs_SetAllLEDs(ConfigSuccess ? LEDMASK_USB_READY : LEDMASK_USB_ERROR); } /** Event handler for the library USB Control Request reception event. */ void EVENT_USB_Device_ControlRequest(void) { Audio_Device_ProcessControlRequest(&Speaker_Audio_Interface); } /** Audio class driver callback for the setting and retrieval of streaming endpoint properties. This callback must be implemented * in the user application to handle property manipulations on streaming audio endpoints. * * When the DataLength parameter is NULL, this callback should only indicate whether the specified operation is valid for * the given endpoint index, and should return as fast as possible. When non-NULL, this value may be altered for GET operations * to indicate the size of the retrieved data. * * note The length of the retrieved data stored into the Data buffer on GET operations should not exceed the initial value * of the c DataLength parameter. * * param[in,out] AudioInterfaceInfo Pointer to a structure containing an Audio Class configuration and state. * param[in] EndpointProperty Property of the endpoint to get or set, a value from Audio_ClassRequests_t. * param[in] EndpointAddress Address of the streaming endpoint whose property is being referenced. * param[in] EndpointControl Parameter of the endpoint to get or set, a value from Audio_EndpointControls_t. * param[in,out] DataLength For SET operations, the length of the parameter data to set. For GET operations, the maximum * length of the retrieved data. When NULL, the function should return whether the given property * and parameter is valid for the requested endpoint without reading or modifying the Data buffer. * param[in,out] Data Pointer to a location where the parameter data is stored for SET operations, or where * the retrieved data is to be stored for GET operations. * * return Boolean c true if the property get/set was successful, c false otherwise */ bool CALLBACK_Audio_Device_GetSetEndpointProperty(USB_ClassInfo_Audio_Device_t* const AudioInterfaceInfo, const uint8_t EndpointProperty, const uint8_t EndpointAddress, const uint8_t EndpointControl, uint16_t* const DataLength, uint8_t* Data) { /* Check the requested endpoint to see if a supported endpoint is being manipulated */ if (EndpointAddress == Speaker_Audio_Interface.Config.DataOUTEndpoint.Address) { /* Check the requested control to see if a supported control is being manipulated */ if (EndpointControl == AUDIO_EPCONTROL_SamplingFreq) { switch (EndpointProperty) { case AUDIO_REQ_SetCurrent: /* Check if we are just testing for a valid property, or actually adjusting it */ if (DataLength != NULL) { /* Set the new sampling frequency to the value given by the host */ CurrentAudioSampleFrequency = (((uint32_t)Data[2] << 16) | ((uint32_t)Data[1] << 8) | (uint32_t)Data[0]); /* Adjust sample reload timer to the new frequency */ OCR0A = ((F_CPU / 8 / CurrentAudioSampleFrequency) - 1); } return true; case AUDIO_REQ_GetCurrent: /* Check if we are just testing for a valid property, or actually reading it */ if (DataLength != NULL) { *DataLength = 3; Data[2] = (CurrentAudioSampleFrequency >> 16); Data[1] = (CurrentAudioSampleFrequency >> 8); Data[0] = (CurrentAudioSampleFrequency & 0xFF); } return true; } } } return false; } /** Audio class driver callback for the setting and retrieval of streaming interface properties. This callback must be implemented * in the user application to handle property manipulations on streaming audio interfaces. * * When the DataLength parameter is NULL, this callback should only indicate whether the specified operation is valid for * the given entity and should return as fast as possible. When non-NULL, this value may be altered for GET operations * to indicate the size of the retrieved data. * * note The length of the retrieved data stored into the Data buffer on GET operations should not exceed the initial value * of the c DataLength parameter. * * param[in,out] AudioInterfaceInfo Pointer to a structure containing an Audio Class configuration and state. * param[in] Property Property of the interface to get or set, a value from Audio_ClassRequests_t. * param[in] EntityAddress Address of the audio entity whose property is being referenced. * param[in] Parameter Parameter of the entity to get or set, specific to each type of entity (see USB Audio specification). * param[in,out] DataLength For SET operations, the length of the parameter data to set. For GET operations, the maximum * length of the retrieved data. When NULL, the function should return whether the given property * and parameter is valid for the requested endpoint without reading or modifying the Data buffer. * param[in,out] Data Pointer to a location where the parameter data is stored for SET operations, or where * the retrieved data is to be stored for GET operations. * * return Boolean c true if the property GET/SET was successful, c false otherwise */ bool CALLBACK_Audio_Device_GetSetInterfaceProperty(USB_ClassInfo_Audio_Device_t* const AudioInterfaceInfo, const uint8_t Property, const uint8_t EntityAddress, const uint16_t Parameter, uint16_t* const DataLength, uint8_t* Data) { /* No audio interface entities in the device descriptor, thus no properties to get or set. */ return false; }
###
Circuit Diagrams
Project Datasheet
Project Video
Filed Under: Electronic Projects
Filed Under: Electronic Projects
Questions related to this article?
👉Ask and discuss on EDAboard.com and Electro-Tech-Online.com forums.
Tell Us What You Think!!
You must be logged in to post a comment.