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.
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.