AN00248: Using lib_xua with lib_mic_array£££doc/rst/an00248.html#an00248-using-lib-xua-with-lib-mic-array

AN00248: Using lib_xua with lib_mic_array$$$Introduction£££doc/rst/an00248.html#introduction

The XMOS USB Audio library lib_xua provides an implementation of USB Audio Class versions 1.0 and 2.0.

This application note demonstrates the implementation of a basic USB Audio Device with record functionality from PDM microphones on the XK-EVK-XU316 board.

Core PDM microphone functionality is contained in lib_mic_array. This library includes both the physical interfacing to the PDM microphones as well as efficient decimation to user configurable output sample rates - essentially providing PDM to PCM conversion.

I2S is also enabled as an output and the on board DAC is configured to play out the mic signals.

AN00248: Using lib_xua with lib_mic_array$$$Example application£££doc/rst/an00248.html#example-application

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$The CMakeLists.txt file£££doc/rst/an00248.html#the-cmakelists-txt-file

Add lib_xua to the dependent module list in the CMakeLists.txt file. Also add lib_board_support which contains code to setup the audio hardware:

set(APP_DEPENDENT_MODULES "lib_xua",
                          "lib_board_support(1.1.1))

Compiler flags need to be set to tell lib_board_support which board is being used. For example:

set(APP_COMPILER_FLAGS =    -DBOARD_SUPPORT_BOARD=XK_EVK_XU316
                            ..)

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$Declarations£££doc/rst/an00248.html#declarations

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$Declarations$$$Allocating hardware resources for lib_xua£££doc/rst/an00248.html#allocating-hardware-resources-for-lib-xua

This implementation of a USB Audio device using lib_xua requires the following I/O pins:

  • Audio Master clock (generated by the XCORE)

  • Bit clock (generated by the XCORE)

  • Word clock (generated by the XCORE)

  • I2S data output pin (generated by the XCORE)

On an XCORE the pins are controlled by ports. The main application therefore declares a port for the master clock input signal. The main() function that is provided within lib_xua/src/core/main.xc itself has these ports defined. Look for p_i2s_dac, p_lrclk, p_bclk and p_mclk_in in lib_xua/src/core/main.xc.

lib_xua also requires two ports for internally calculating USB feedback. Please refer to the lib_xua library documentation for further details. In this example XUA_Buffer() and XUA_AudioHub() reside on the same tile and can therefore make use of the same master-clock port. These are defined as p_for_mclk_count and p_mclk_in_usb in the main() function in lib_xua.

In addition to port resources, some clock-block resources (clk_audio_bclk, clk_audio_mclk and clk_audio_mclk_usb), also defined in the main() function in lib_xua, are required.

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$Declarations$$$Allocating hardware resources for lib_mic_array£££doc/rst/an00248.html#allocating-hardware-resources-for-lib-mic-array

In this example lib_mic_array requires a single 1-bit port for PDM data from two microphones. Each microphone is configured to produce a PDM sample on an opposite clock edge from the other. This means the the data is effectively double data rate (DDR) with respect to the PDM clock.

The microphones must be clocked by a clock synchronous to the audio application clock - typically 3.072MHz for 16, 32 or 48 kHz.

These ports, along with other mic_array required resources must be declared in mic_array_conf.h:

#define XUA_NUM_PDM_MICS                    2
#define MIC_ARRAY_CONFIG_PORT_MCLK          PORT_MCLK_IN
#define MIC_ARRAY_CONFIG_PORT_PDM_CLK       PORT_PDM_CLK
#define MIC_ARRAY_CONFIG_PORT_PDM_DATA      PORT_PDM_DATA
#define MIC_ARRAY_CONFIG_CLOCK_BLOCK_A      XS1_CLKBLK_1
#define MIC_ARRAY_CONFIG_CLOCK_BLOCK_B      XS1_CLKBLK_2    /* Second clock needed as we use DDR */

The XK-EVK-XU316 Board expects the XCORE to divide down the audio master clock (24.576MHz) and output the result to the microphones. This is done internally by lib_mic_array from the definitions MIC_ARRAY_CONFIG_MCLK_FREQ and MIC_ARRAY_CONFIG_PDM_FREQ which are left as defaults in mic_array_conf.h to generate the nominal 3.072 MHz PDM clock.

Please see the lib_mic_array library documentation for full details.

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$Configuring lib_xua£££doc/rst/an00248.html#configuring-lib-xua

lib_xua must be configured to enable support for PDM microphones.

lib_xua has many parameters than can be configured at build time, some examples include:

  • Supported sample-rates

    Note

    lib_mic_array does not currently support sample rate change after initialisation

  • Channel counts

  • Audio Class version

  • Product/Vendor IDs

  • Various product strings

  • Master clock frequency

To enable PDM microphone support XUA_NUM_PDM_MICS must be set to a non-zero value. Setting this will cause the XUA_AudioHub task to forward sample rate information and receive samples from the relevant microphone related tasks.

These parameters are set via defines in the xua_conf.h header file. For this simple application the complete contents of this file are as follows:

// Copyright 2017-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.

#ifndef _XUA_CONF_H_
#define _XUA_CONF_H_

#include <platform.h>

#define NUM_USB_CHAN_OUT                    0                /* Number of channels from host to device */
#define NUM_USB_CHAN_IN                     2                /* Number of channels from device to host */
#define I2S_CHANS_DAC                       2                /* Number of I2S channels out of xCORE */
#define I2S_CHANS_ADC                       0                /* Number of I2S channels in to xCORE */
#define MCLK_441                            (512 * 44100)    /* 44.1kHz family master clock frequency */
#define MCLK_48                             (512 * 48000)    /* 48kHz family master clock frequency */
#define XUA_PDM_MIC_FREQ                    48000            /* Currently sample rate changes are not supported for PDM mics */
#define MIN_FREQ                            XUA_PDM_MIC_FREQ /* Minimum sample rate */
#define MAX_FREQ                            XUA_PDM_MIC_FREQ /* Maximum sample rate */


#define PORT_MCLK_IN_USB 					XS1_PORT_1D
#define PORT_MCLK_COUNT 					XS1_PORT_16B
#define PORT_I2S_DAC0						PORT_I2S_DAC_DATA

/* Since the PORT defines are done manually above, not in the XN file, lib_xua must be told
 * which tiles the ports are located */
#define XUA_AUDIO_IO_TILE_NUM               1

#define XUA_NUM_PDM_MICS                    2
#define MIC_ARRAY_CONFIG_PORT_MCLK          PORT_MCLK_IN
#define MIC_ARRAY_CONFIG_PORT_PDM_CLK       PORT_PDM_CLK
#define MIC_ARRAY_CONFIG_PORT_PDM_DATA      PORT_PDM_DATA
#define MIC_ARRAY_CONFIG_CLOCK_BLOCK_A      XS1_CLKBLK_1
#define MIC_ARRAY_CONFIG_CLOCK_BLOCK_B      XS1_CLKBLK_2    /* Second clock needed as we use DDR */

#define AUDIO_CLASS                         1
#define VENDOR_STR                          "XMOS"
#define VENDOR_ID                           0x20B1
#define PRODUCT_STR_A2                      "XUA PDM Example"
#define PRODUCT_STR_A1                      "XUA PDM Example"
#define PID_AUDIO_1                         1
#define PID_AUDIO_2                         2
#define XUA_DFU_EN                          0               /* Disable DFU for simplicity of example */

#endif

XUA_PDM_MIC_FREQ can be changed to 32000 or 16000 to reconfigure the system to lower sample rates than the default 48 kHz.

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$The application main() function£££doc/rst/an00248.html#the-application-main-function

The main() function is provided within the lib_xua/src/core/main.xc file. It starts all of the tasks in parallel using the xC par construct.

Firstly the standard lib_xua USB side tasks are run on tile 0. This code starts the low-level USB task and an Endpoint 0 task. The Audio buffering task and a task to handle the audio I/O (XUA_AudioHub) is started on tile 1 where the I2S bus exists.

The microphone task mic_array_task spawns a single thread which handles PDM receive on the ports and the decimation filters to produce PCM. This is placed on tile 1 where the microphone hardware is connected. It connects directly to XUA_AudioHub and provides samples which are at the same rate as the audio I/O.

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$The application main() function$$$User callbacks£££doc/rst/an00248.html#user-callbacks

While the main() function is provided within lib_xua itself, there are a number of callbacks which allow for customisation of the application.

The DAC is configured by AudioHwInit() function in hwsupport.xc, which calls Audio hardware initialisation functions for the relevant hardware from lib_board_support

#include "xua.h"
#include "xk_evk_xu316/board.h"

void AudioHwInit()
{
    xk_evk_xu316_config_t hw_config = {MCLK_48};
    xk_evk_xu316_AudioHwInit(hw_config);

    const int samFreq = 48000; /* xk_evk_xu316_AudioHwConfig doesn't like rates below 22kHz so force to 48k which works OK */
    xk_evk_xu316_AudioHwConfig(samFreq, MCLK_48, 0, 32, 32);

    return;
}

Callback functions UserBufferManagement() and user_pdm_process() are defined in user_callbacks.xc. Both these functions are called from the main loop in XUA_AudioHub(). user_pdm_process() implements the PCM sample post processing, which for this example is a simple scaling by a factor of 64 to allow the mic captured audio to be heard easily. UserBufferManagement() routes the mic samples to the DAC.

// Copyright 2017-2025 XMOS LIMITED.
// This Software is subject to the terms of the XMOS Public Licence: Version 1.

#include <xs1.h>
#include <platform.h>
#include <string.h>

#include "xua.h"
#include "xud_device.h"

/* Copy mic samples inbound to the host to the DAC so they can be monitored there */
#pragma unsafe arrays
void UserBufferManagement(unsigned sampsFromUsbToAudio[], unsigned sampsFromAudioToUsb[])
{

    for(int i = 0; i < I2S_CHANS_DAC; i++)
    {
        sampsFromUsbToAudio[i] = sampsFromAudioToUsb[i];
    }
}

/* Apply some gain so we can hear the mics easily (non-saturating - will overflow) */
#pragma unsafe arrays
void user_pdm_process(int32_t mic_audio[MIC_ARRAY_CONFIG_MIC_COUNT])
{
    for(int i = 0; i < XUA_NUM_PDM_MICS; i++)
    {
        mic_audio[i] = mic_audio[i] << 6; /* x64 */
    }
}

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$Hardware setup£££doc/rst/an00248.html#hardware-setup

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$Hardware setup$$$Required hardware£££doc/rst/an00248.html#required-hardware

  • 1x XK-EVK-XU316 board

  • 2x Micro USB cable (For USB and XTAG)

  • 3.5mm audio cable to connect to the line out connector on the XK-EVK-XU316 board

  • Host computer

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$Hardware setup$$$Setup procedure£££doc/rst/an00248.html#setup-procedure

  1. Connect a pair of PDM microphones to the microphone connector on the XK-EVK-XU316 via a ribbon cable. Refer to the xcore.ai Explorer hardware manual for more details.

  2. Connect one Micro USB cable between the host computer and the XK-EVK-XU316 DEBUG port.

  3. Connect the other Micro USB cable between the host computer and the XK-EVK-XU316 USB port.

Fig. 1 shows the XK-EVK-XU316 board with all the cables connected.

_images/hw_setup.jpg

Hardware setup

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$Building the example£££doc/rst/an00248.html#building-the-example

This section assumes that the XMOS XTC Tools have been downloaded and installed. The required version is specified in the accompanying README.

Installation instructions can be found here.

Special attention should be paid to the section on Installation of Required Third-Party Tools.

The application is built using the xcommon-cmake build system, which is provided with the XTC tools and is based on CMake.

The an00248 software ZIP package should be downloaded and extracted to a chosen working directory.

To configure the build, the following commands should be run from an XTC command prompt:

cd an00248
cd app_an00248
cmake -G "Unix Makefiles" -B build

All required dependencies are included in the software package. If any dependencies are missing, they will be retrieved automatically during this step.

The application binaries should then be built using xmake:

xmake -j -C build

Binary artifacts (.xe files) will be generated under the appropriate subdirectories of the app_an00248/bin directory — one for each supported build configuration.

For subsequent builds, the cmake step may be omitted. If CMakeLists.txt or other build files are modified, cmake will be re-run automatically by xmake as needed.

The application uses approximately 49 kB on tile 0 and 17 kB on tile 1 (of 512 kB on each).

AN00248: Using lib_xua with lib_mic_array$$$Example application$$$Running the example£££doc/rst/an00248.html#running-the-example

From an XTC command prompt, the following command should be run from the an00248/app_an00248 directory:

xrun ./bin/app_an00248.xe

Alternatively, the application can be programmed into flash memory for standalone execution:

xflash ./bin/app_an00248.xe

A USB audio device called XUA PDM Example should now be seen enumerated and the stereo microphone audio input stream can be recorded on the USB host or listened to by connecting to the analog output jacks.