PDM/PCM (Pulse-Density Modulation to Pulse-Code Modulation Converter)

group group_hal_pdmpcm

High level interface for interacting with the pulse-density modulation to pulse-code modulation (PDM/PCM) converter.

The PDM/PCM converter is a asynchronous operation. A PDM-PCM converter is used to convert 1-bit digital audio streaming data to PCM data. The sample rate, word size, and channels can all be configured.

Features

  • Supports FIFO buffer for Incoming Data

  • Configurable Gain Settings

  • Configurable Word Length

  • Configurable interrupt and callback assignment from PDM/PCM events - cyhal_pdm_pcm_event_t

Quick Start

Initialize a PDM/PCM converter instance using the cyhal_pdm_pcm_init and provide the clock and data pins.

See

Snippet 1: PDM/PCM Initialization and Configuration for example initialization.

note

The clock parameter (const cyhal_clock_t *clk) is optional and can be set to NULL to generate and use an available clock resource with a default frequency.

Code Snippets

note

Error checking is omitted for clarity

Snippet 1: PDM/PCM Initialization and Configuration

This snippet initializes a PCM/PCM resource for conversion and assigns the pins.

    cyhal_pdm_pcm_t     pdm_pcm;
    cyhal_pdm_pcm_cfg_t cfg =
    {
        .sample_rate     = 44000,
        .decimation_rate = 10,
        .mode            = CYHAL_PDM_PCM_MODE_STEREO,
        .word_length     = 16,
        .left_gain       = 1,  // +0.5 db gain
        .right_gain      = -2, // -1.0 db gain
    };
    cy_rslt_t result = cyhal_pdm_pcm_init(&pdm_pcm, P0_5, P0_4, NULL, &cfg);
    CY_ASSERT(CY_RSLT_SUCCESS == result);

    // When the PDM/PCM instance is no longer required, free it to release resources
    cyhal_pdm_pcm_free(&pdm_pcm);

Snippet 2: PDM/PCM Asynchronous Receive

This snippet shows how to receive data in the background using cyhal_pdm_pcm_read_async. Notification of the asynchronous read completion is achieved by using cyhal_pdm_pcm_register_callback to register a callback function and cyhal_pdm_pcm_enable_event to enable callling the callback when an synchonous read completes.

// We use a dual buffer system so that one buffer can be filling while the other is being processed
#define BUFFER_SIZE 128u
static uint32_t  rx_buffer0[BUFFER_SIZE];
static uint32_t  rx_buffer1[BUFFER_SIZE];
static uint32_t* active_rx_buffer;
static uint32_t* full_rx_buffer;
static void pdm_pcm_event_handler(void* arg, cyhal_pdm_pcm_event_t event)
{
    // When we registered the callback, we set 'arg' to point to the pdm_pcm object
    cyhal_pdm_pcm_t* pdm_pcm = (cyhal_pdm_pcm_t*)arg;
    if (0u != (event & CYHAL_PDM_PCM_ASYNC_COMPLETE))
    {
        // Flip the active and the next rx buffers
        uint32_t* temp = active_rx_buffer;
        active_rx_buffer = full_rx_buffer;
        full_rx_buffer   = temp;

        // Start reading into the next buffer while the just-filled one is being processed
        cy_rslt_t result = cyhal_pdm_pcm_read_async(pdm_pcm, active_rx_buffer, BUFFER_SIZE);
        CY_ASSERT(CY_RSLT_SUCCESS == result);

        // Process the data in the full_rx_buffer
    }
}


void snippet_cyhal_pdm_pcm_async_receive(void)
{
    cyhal_pdm_pcm_t pdm_pcm;
    // Initialize the object as shown in Snippet 1

    // Register a callback and set the callback argument to be a pointer to the PDM/PCM object, so
    // that we can easily reference it from the callback handler.
    cyhal_pdm_pcm_register_callback(&pdm_pcm, &pdm_pcm_event_handler, &pdm_pcm);

    // Subscribe to the async complete event so that we can queue up another transfer when this one
    // completes
    cyhal_pdm_pcm_enable_event(&pdm_pcm, CYHAL_PDM_PCM_ASYNC_COMPLETE, CYHAL_ISR_PRIORITY_DEFAULT,
                               true);

    // Configure asynchronous transfers to use DMA to free up the CPU during transfers
    cy_rslt_t result = cyhal_pdm_pcm_set_async_mode(&pdm_pcm, CYHAL_ASYNC_DMA,
                                                    CYHAL_DMA_PRIORITY_DEFAULT);
    CY_ASSERT(CY_RSLT_SUCCESS == result);

    active_rx_buffer = rx_buffer0;
    full_rx_buffer   = rx_buffer1;
    result           = cyhal_pdm_pcm_read_async(&pdm_pcm, active_rx_buffer, BUFFER_SIZE);

    result = cyhal_pdm_pcm_start(&pdm_pcm);
}

More Information

Code examples (Github)

Typedefs

typedef void (*cyhal_pdm_pcm_event_callback_t)(void *handler_arg, cyhal_pdm_pcm_event_t event)

Handler for PDM/PCM interrupts.

Enums

enum cyhal_pdm_pcm_event_t

cyhal_pdm_pcm_event_t: PDM/PCM interrupt triggers.

Values:

enumerator CYHAL_PDM_PCM_RX_HALF_FULL

RX hardware buffer is half full.

enumerator CYHAL_PDM_PCM_RX_NOT_EMPTY

RX hardware buffer is not empty.

enumerator CYHAL_PDM_PCM_RX_OVERFLOW

Attempt to write to a full RX hardware buffer.

enumerator CYHAL_PDM_PCM_RX_UNDERFLOW

Attempt to read from an empty buffer.

enumerator CYHAL_PDM_PCM_ASYNC_COMPLETE

Async operation completed.

enum cyhal_pdm_pcm_mode_t

cyhal_pdm_pcm_mode_t: PDM/PCM channel select.

Values:

enumerator CYHAL_PDM_PCM_MODE_LEFT

The channel mono left.

enumerator CYHAL_PDM_PCM_MODE_RIGHT

The channel mono right.

enumerator CYHAL_PDM_PCM_MODE_STEREO

The channel stereo.

Functions

cy_rslt_t cyhal_pdm_pcm_init(cyhal_pdm_pcm_t *obj, cyhal_gpio_t pin_data, cyhal_gpio_t pin_clk, const cyhal_clock_t *clk_source, const cyhal_pdm_pcm_cfg_t *cfg)

Initialize the PDM/PCM peripheral.

Configures the pins used by PDM/PCM converter, sets a default format and frequency, and enables the peripheral

Return

The status of the init request

Parameters
  • [out] obj: Pointer to a PDM/PCM object. The caller must allocate the memory for this object but the init function will initialize its contents.

  • [in] pin_data: The pin to use for PDM input

  • [in] pin_clk: The pin to use for PDM clock output

  • [in] clk_source: The clock source for PDM/PCM block

  • [in] cfg: The configuration for the PDM/PCM block

void cyhal_pdm_pcm_free(cyhal_pdm_pcm_t *obj)

Release a PDM/PCM object, behavior is undefined if an asynchronous read is in progress.

Return the peripheral, pins and clock owned by the PDM/PCM object to their reset state

Parameters
  • [inout] obj: The PDM/PCM object to deinitialize

cy_rslt_t cyhal_pdm_pcm_start(cyhal_pdm_pcm_t *obj)

Start the PDM/PCM operation.

Return

the status of the start request

Parameters
  • [in] obj: The PDM/PCM object to start

cy_rslt_t cyhal_pdm_pcm_stop(cyhal_pdm_pcm_t *obj)

Stop the PDM/PCM operation.

Return

the status of the stop request

Parameters
  • [in] obj: The PDM/PCM object to start

bool cyhal_pdm_pcm_is_enabled(cyhal_pdm_pcm_t *obj)

Checks if the specified PDM/PCM peripheral is enabled (regardless of whether any unread data has been received).

The PDM/PCM peripheral can be enabled by calling cyhal_pdm_pcm_start and disabled by calling cyhal_pdm_pcm_stop

Return

Whether the I2S receive function is enabled.

Parameters
  • [in] obj: The I2S peripheral to check

cy_rslt_t cyhal_pdm_pcm_set_gain(cyhal_pdm_pcm_t *obj, int8_t gain_left, int8_t gain_right)

Updates the PDM/PCM channel gains.

Each integer increment represent a 0.5 dB value. For example: a gain value of 5 would mean +2.5 dB. If the exact gain value requested is not supported, it will be rounded to the nearest legal value. See the implementation specific documentation for valid ranges.

note

Gains may be negative if the implementation supports it.

Return

The status of the set gain operation. An error will be returned if the value is outside of range supported by HW.

Parameters
  • [in] obj: The PDM/PCM object to configure

  • [in] gain_left: The gain of the left channel in units of 0.5 dB

  • [in] gain_right: The gain of the right channel in units of 0.5 dB

cy_rslt_t cyhal_pdm_pcm_clear(cyhal_pdm_pcm_t *obj)

Clears the hardware buffer.

Return

The status of the clear request

Parameters
  • [in] obj: The PDM/PCM peripheral

cy_rslt_t cyhal_pdm_pcm_read(cyhal_pdm_pcm_t *obj, void *data, size_t *length)

Reads data synchronously.

This will read either length words or the number of words that are currently available in the receive buffer, whichever is less, then return. The value pointed to by length will be updated to reflect the number of words that were actually read. If there are less data in FIFO than length, length will be update with number of words read.

Return

The status of the read request

Parameters
  • [in] obj: The PDM/PCM peripheral

  • [out] data: Pointer to word array where incoming data will be stored. Buffer must be aligned to word-size. Each word will be aligned to the next largest power of 2. For example, if the word length is 16 bits, each word will consume two bytes. But if the word length is 20, each word will consume 32 bits. Negative value will use sign-extension. -1 with 24-bit word length will have 32-bit value of 0xFFFFFFFF.

  • [inout] length: Number of 32-bit words to read, updated with the number actually read

cy_rslt_t cyhal_pdm_pcm_read_async(cyhal_pdm_pcm_t *obj, void *data, size_t length)

Begin asynchronous PDM/PCM read.

This will transfer length words into the buffer pointed to by data in the background. When the requested quantity of data has been read, the CYHAL_PDM_PCM_ASYNC_COMPLETE event will be raised. See cyhal_pdm_pcm_register_callback and cyhal_pdm_pcm_enable_event.

cyhal_pdm_pcm_set_async_mode can be used to control whether this uses DMA or a CPU-driven transfer.

Return

The status of the read_async request

Parameters
  • [in] obj: The PDM/PCM object

  • [out] data: Pointer to word array where incoming data will be stored. Buffer must be aligned to word-size. Each word will be aligned to the next largest power of 2. For example, if the word length is 16 bits, each word will consume two bytes. But if the word length is 20, each word will consume 32 bits. Negative value will use sign-extension. -1 with 24-bit word length will have 32-bit value of 0xFFFFFFFF.

  • [in] length: Number of words to read

bool cyhal_pdm_pcm_is_pending(cyhal_pdm_pcm_t *obj)

Checks if an async read operation is pending.

Return

Indication of whether a PDM/PCM async operation is pending

Parameters
  • [in] obj: The PDM/PCM peripheral to check

cy_rslt_t cyhal_pdm_pcm_abort_async(cyhal_pdm_pcm_t *obj)

Abort an PDM/PCM operation started by cyhal_pdm_pcm_read_async function.

Return

The status of the abort_async request

Parameters
  • [in] obj: The PDM/PCM peripheral to stop

void cyhal_pdm_pcm_register_callback(cyhal_pdm_pcm_t *obj, cyhal_pdm_pcm_event_callback_t callback, void *callback_arg)

Register a PDM/PCM event handler.

This function will be called when one of the events enabled by cyhal_pdm_pcm_enable_event occurs.

Parameters
  • [in] obj: The PDM/PCM object

  • [in] callback: The callback handler which will be invoked when the interrupt fires

  • [in] callback_arg: Generic argument that will be provided to the callback when called

void cyhal_pdm_pcm_enable_event(cyhal_pdm_pcm_t *obj, cyhal_pdm_pcm_event_t event, uint8_t intr_priority, bool enable)

Configure PDM/PCM event enablement.

Parameters
  • [in] obj: The PDM/PCM object

  • [in] event: The PDM/PCM event type

  • [in] intr_priority: The priority for NVIC interrupt events

  • [in] enable: True to turn on events, False to turn off

cy_rslt_t cyhal_pdm_pcm_set_async_mode(cyhal_pdm_pcm_t *obj, cyhal_async_mode_t mode, uint8_t dma_priority)

Set the mechanism that is used to perform PDM/PCM asynchronous operation.

The default is SW.

When an enabled event occurs, the function specified by cyhal_pdm_pcm_register_callback will be called.

Return

The status of the set mode request

Parameters
  • [in] obj: The PDM/PCM object

  • [in] mode: The transfer mode

  • [in] dma_priority: The priority, if DMA is used. Valid values are the same as for cyhal_dma_init. If DMA is not selected, the only valid value is CYHAL_DMA_PRIORITY_DEFAULT, and no guarantees are made about prioritization.

struct cyhal_pdm_pcm_cfg_t
#include <cyhal_pdmpcm.h>

Describes the current configuration of a PDM/PCM.

Public Members

uint32_t sample_rate

Sample rate in Hz.

uint8_t decimation_rate

PDM decimation rate.

cyhal_pdm_pcm_mode_t mode

left, right, or stereo

uint8_t word_length

PCM word length in bits, see the implementation specific documentation for valid range.

int8_t left_gain

PGA in 0.5 dB increment, for example a value of 5 would mean +2.5 dB.

The closest fit value will be used, see the implementation specific documentation for valid ranges. This may be negative if the implementation supports it.

int8_t right_gain

PGA in 0.5 dB increment, for example a value of 5 would mean +2.5 dB.

The closest fit value will be used, see the implementation specific documentation for valid ranges. This may be negative if the implementation supports it.