Hide navigation sidebar
Hide table of contents sidebar
Skip to content
Back to top
Toggle table of contents sidebar

# PDM microphones

`lib_xua` is capable of integrating with PDM microphones. The PDM stream from the microphones is converted to PCM and output to the host via USB.

Interfacing to the PDM microphones is done using the XMOS microphone array library (lib_mic_array). `lib_mic_array` is designed to allow interfacing to PDM microphones coupled to efficient decimation filters at a user configurable output sample rate.

Note

The `lib_mic_array` library is only available for xcore.ai series devices since it uses the Vector Processing Unit of the XS3 architecture.

Up to eight PDM microphones can be attached to the PDM interface (`mic_array_task()`) but it is possible to extend this.

After PDM capture and decimation to the output sample-rate various other steps take place e.g. DC offset elimination etc. Please refer to the documentation provided with lib_mic_array for further implementation detail and a complete feature set.

By default the sample rates supported are 16 kHz, 32 kHz and 48 kHz although other rates are supportable with some modifications.

Please see AN00248 Using lib_xua with lib_mic_array for a practical example of this feature.

## Hardware characteristics

The PDM microphones require a clock input and provide the PDM signal on a data output. All of the PDM microphones must share the same clock signal (buffered on the PCB as appropriate), and output onto the data wire(s) that are connected to the capture port. The lines required to interface with PDM microphones are listed in Table 41.

Table 41 PDM microphone data and signal wires
Signal | Description
--- | ---
CLOCK | The PDM clock the used by the microphones to drive the data out.
DQ_PDM | The data from the PDM microphones on the capture port.

Note

The clocking for PDM microphones may be single data rate (one microphone per pin) or double data rate (two microphones per pin clocking on alternate edges). By default `lib_xua` assumes double data rate which provides more efficient port usage.

When initialising the mic array, a `pdm_rx_resources_t` structure is passed to `mic_array_init()`. This contains the hardware resource IDs required for PDM capture:

    unsigned mClk = MCLK_48, pdmClk = 3072000;
    #if (XUA_PDM_MIC_USE_DDR)
          pdm_rx_resources_t pdm_res = PDM_RX_RESOURCES_DDR(
              PORT_MCLK_IN,
              PORT_PDM_CLK,
              PORT_PDM_DATA,
              mClk,
              pdmClk,
              XS1_CLKBLK_1,
              XS1_CLKBLK_2);
      #else
          pdm_rx_resources_t pdm_res = PDM_RX_RESOURCES_SDR(
              PORT_MCLK_IN,
              PORT_PDM_CLK,
              PORT_PDM_DATA,
              mClk,
              pdmClk,
              XS1_CLKBLK_1);
      #endif
          mic_array_init(&pdm_res, NULL, mic_samp_rate);

For full details of the `pdm_rx_resources_t` structure and mic array initialisation, please refer to the lib_mic_array documentation.

## Usage & integration

### Mic array task

A PDM microphone wrapper, `mic_array_task()`, is called from `main()`. It takes one channel argument connecting it to the rest of the system, and a `channel_map` specifying the mapping from PDM input pins to microphone output channels:

    on stdcore[XUA_MIC_PDM_TILE_NUM]:
    {
        for(int i=0; i<XUA_NUM_PDM_MICS; i++) {
            mic_array_channel_map[i] = i;
        }
        mic_array_task(c_pdm_pcm, mic_array_channel_map);
    }

The implementation of `mic_array_task()` can be found in the file `mic_array_task.c`. It typically takes one hardware thread.

`mic_array_task()` runs a `while(1)` loop. At the start of each iteration, it calls `xua_user_pdm_init()`, which is an optional user-provided function that may be used to update `channel_map` if a non–1:1 mapping between PDM input pins and microphone output channels is required.

It then waits to receive the current sampling rate over the `c_pdm_pcm` channel. Once received, it initialises the mic array for the requested sampling rate (using `mic_array_init()`) and then starts the mic array thread(s) via `mic_array_start()`. `mic_array_start()` launches either one or two hardware threads, depending on the value of `XUA_PDM_MIC_USE_PDM_ISR`.

The `c_pdm_pcm` channel is passed to the mic array decimator thread, over which it sends decimated PCM frames to `XUA_AudioHub()`.

Note, it is assumed that the system shares a global master-clock, therefore no additional buffering or rate-matching/conversion is required. This ensures the PDM subsystem and XUA Audiohub are synchronous.

### Receiving PCM samples

`XUA_AudioHub's` main IO loop (`AudioHub_MainLoop()`) calls the mic array function `ma_frame_rx()` using the other end of the channel passed to the mic array thread to receive PCM frames from the mic array:

    ma_frame_rx(mic_samps_base_addr, c_m2a, MIC_ARRAY_CONFIG_SAMPLES_PER_FRAME, MIC_ARRAY_CONFIG_MIC_COUNT);

Note that the interface between mic array and xua is sample based - so the only supported value of MIC_ARRAY_CONFIG_SAMPLES_PER_FRAME is 1.

### Restarting the mic array

If `AudioHub_MainLoop()` function exits, the mic array is shut down by calling `ma_shutdown()` from `XUA_AudioHub()`:

    ma_shutdown((chanend_t)c_pdm_in); // shutdown mics

Note, that the channel passed to `ma_shutdown()` is the same one used by `ma_frame_rx()` to receive PCM frames, so care must be taken to ensure that `ma_frame_rx()` is not being called concurrently when `ma_shutdown()` is invoked.

Calling `ma_shutdown()` causes the mic array thread(s) to terminate. When this happens, the `mic_array_start()` call inside `mic_array_task()` returns, after which `mic_array_task()` waits again to receive the next sampling-rate value on the channel before restarting the mic-array thread(s).

### Weak callback APIs

Two weak callback APIs - `xua_user_pdm_init()` and `xua_user_pdm_process()`, are provided, which optionally allow user code to be executed at startup (post PDM microphone initialisation) and after each sample frame is formed.