Created by: Group 11 - Sean Hunter, Michael Wong, Thomas Zylstra
Description: This application note details configuration required to use the audio core from Altera's University IP Cores to interface with the WM8731 on the DE2. The example implemented in this application note simply passes the audio signal from MIC IN to LINE OUT, with basic software processing to clean up the signal. Using the University IP Cores offers the significant advantage of access to Altera's HAL API calls, which provide a high-level software interface to working with and controlling the audio codec.

This application note applies to Quartus II v10.1, and uses the SOPC Builder tool. The software IDE used is the Nios II IDE Version 10.1sp1, Build 197.
Hardware Configuration: There are three components that must be added to the system. The first is the Audio Core itself. This core provides a serializer/deserializer to interface with the WM8731, as well as incoming and outgoing FIFOs for the left and right channel data. It interfaces with the Nios II as a MM slave. The second is the Audio and Video Configuration Core. This core is responsible for the I2C interface with the WM8731, and is therefore used to control the codec chip (writing to configuration registers, configuring interrupt behaviour, etc.). It also interfaces with the Nios II as a MM slave. Finally, there is a Clocks Signals core, which is responsible for providing the clock signal to the audio cores.

The Audio Core and the Audio and Video Configuration Core require a separate clock that is different from the system clock. This example uses a sampling rate of 32kHz, so from the WM8731 datasheet, the clock signal should be 12.288MHz. The Clocks Signals core that provides this from its audio_clk output. However, it needs a 27MHz clock as an input to clk_in_secondary to produce this, which is shown in the image below as clk_1. Note that the Clocks Signals core can also produce a secondary system clock output from clk_in_primary, but this is not needed for this example. Ensure that your top-level entity has the two clock ports, the two I2C ports, and the six audio signal ports.

Software Operation: Implementation of a task that passes MIC IN data to LINEOUT shown in the attached software/main.c, and below. This assumes 16-bit audio, sampled at a rate of 32kHz. Note the use of HAL API calls. The Audio Core and A/V Config Core devices are first opened and reset. The WM8731 is then configured using the alt_up_av_config_write_audio_cfg_register() call. Some of these register writes may not be necessary due to auto-initialization (where values are taken from the values specified for the component in the SOPC builder), but are included for the sake of completeness.

After the initial set-up, the program enters the loop that performs audio data movement. We use a software buffer here that can store 128 32-bit values to faciliate data transfer. 128 was chosen because the FIFOs in the Audio Core hold 128 samples. In the loop, we first read all data present in the incoming buffer and store it in our software buffer l_buf. The newly read chunk of data is then subjected to simple processing (see below), and is then written to the outgoing FIFOs of the Audio Core. Note that in order for audio playback to occur, both the left and right outgoing FIFOs of the Audio Core must contain data. We can obtain playback of mono sound by either writing the same data to both FIFOs, or by writing zeroes to one of the channels.



Special attention should be drawn to the preliminary shift of all values by half the range of 16-bit values, or 0x7fff. This is necessary because the audio-to-digital conversion performed by the WM8731 produces values around a DC offset of 0, which means that approximately half of the samples values will be negative, as shown in the histogram above. This results in an overflow to a value (2^32)-x for all negative values, as the Audio Core passes unsigned integers to the Audio Codec, which manifests itself as loud, high-pitched static noise on LINEOUT. By performing this shift of 0x7fff, we move the DC offset to 32767 so that for the range of 16-bit integers, it is safe for the codec to consider all values as positive.

#define     BUFFER_SIZE    128


/* Handle audio data movement */
void audio_data_task(void* pdata)
{
    alt_up_audio_dev * audio_dev;
    alt_up_av_config_dev * audio_config_dev;   

    unsigned int l_buf[BUFFER_SIZE];
    int i = 0;
    int writeSizeL = 0;

    /* Open Devices */
    audio_dev = alt_up_audio_open_dev ("/dev/audio_0");
    if ( audio_dev == NULL)
        printf("Error: could not open audio device \n");
    else
        printf("Opened audio device \n");

    audio_config_dev = alt_up_av_config_open_dev("/dev/audio_and_video_config_0");
    if ( audio_config_dev == NULL)
        printf("Error: could not open audio config device \n");
    else
        printf("Opened audio config device \n");
  
    /* Configure WM8731 */
    alt_up_audio_reset_audio_core(audio_dev);
    alt_up_av_config_reset(audio_config_dev);

    /* Write to configuration registers in the audio codec; see datasheet for what these values mean */
    alt_up_av_config_write_audio_cfg_register(audio_config_dev, 0x0, 0x17);
    alt_up_av_config_write_audio_cfg_register(audio_config_dev, 0x1, 0x17);
    alt_up_av_config_write_audio_cfg_register(audio_config_dev, 0x2, 0x79);
    alt_up_av_config_write_audio_cfg_register(audio_config_dev, 0x3, 0x79);
    alt_up_av_config_write_audio_cfg_register(audio_config_dev, 0x4, 0x15);
    alt_up_av_config_write_audio_cfg_register(audio_config_dev, 0x5, 0x06);
    alt_up_av_config_write_audio_cfg_register(audio_config_dev, 0x6, 0x00);
    
    //main loop
    while(1)
    {
            //read the data from the left buffer
            writeSizeL = alt_up_audio_read_fifo(audio_dev, l_buf, BUFFER_SIZE, ALT_UP_AUDIO_LEFT);

            //shift values to a proper base value
            for (i = 0; i < writeSizeL; i = i+1)
            {
                l_buf[i] = l_buf[i] + 0x7fff;
            } 
        
            //write data to the L and R buffers; R buffer will receive a copy of L buffer data
            alt_up_audio_write_fifo (audio_dev, l_buf, writeSizeL, ALT_UP_AUDIO_RIGHT);
            alt_up_audio_write_fifo (audio_dev, l_buf, writeSizeL, ALT_UP_AUDIO_LEFT);
        
    }        

}
Files:
Instructions for Reproducing:
  1. Copy all files in this directory (except index.html, histogram.png, hw_config.png) into the project folder
  2. Run scripts/launch_quartus.sh
  3. Create a new project; using the New Project Wizard, name the new project AudioTest, add AudioTest.vhd (the top level file), and configure for operation with the Altera DE2
  4. Open the SOPC Builder and open the file AudioTest.sopc
  5. Generate the system
  6. In Quartus, add all .v and .vhd files except for niosII_system_inst.vhd
  7. Set AudioTest.vhd as the top-level
  8. Import pin assignments, using pin_assignments/DE2_pin_assignments_all.csv
  9. Compile
  10. Run scripts/reconnect_jtag.sh and program the board with the generated AudioTest.sof (a precompiled .sof is also present in compiled/AudioTest.pof)
  11. Run scripts/launch_nios2_ide.sh
  12. Create a new MicroC OS project, setting the software directory and the niosII_system.ptf file
  13. Set the syslib properties to use sram_controller for all memory locations
  14. Copy the contents of software/main.c to the main C file of the newly created project
  15. Build the program and run
  16. Connect speakers to LINE OUT and a microphone to MIC IN; sound captured by the microphone should be played through the speakers
References:
  • Audio Core for Altera DE-Series Boards for Quartus II 9.1 Documentation, Altera
  • Audio/Video Configuration Core for DE-Series Boards for Quartus II 9.1 Documentation, Altera
  • WM8731/WM8731L Datasheet, Wolfson Microelectronics