PWM Audio

[中文]

Supported Socs

ESP32

ESP32-S2

ESP32-S3

ESP32-C3

The PWM audio function uses the internal LEDC peripheral in ESP32 to generate PWM audio, which does not need an external audio Codec chip. This is mainly used for cost-sensitive applications with low audio quality requirements.

Features

  • Allows any GIPO with output capability as an audio output pin

  • Supports 8-bit ~ 10-bit PWM resolution

  • Supports stereo

  • Supports 8 ~ 48 KHz sampling rate

Structure

../_images/pwm_audio_diagram.png

Structure

  1. First, the data is recoded to meet the PWM input requirements, including the shift and offset of the data;

  2. Send data to the ISR (Interrupt Service Routines) function of the Timer Group via Ring Buffer;

  3. The Timer Group reads data from the Ring Buffer according to the pre-defined sampling rate, and write the data into LEDC.

Note

Since the output is a PWM signal, it needs to be low-pass filtered to get the audio waveform.

PWM Frequency

The frequency of the output PWM cannot be configured directly, but needs to be calculated by configuring the number of PWM resolution bits, as shown below:

\[f_{pwm}=\frac{f_{APB\_CLK}}{2^{res\_bits}}-\left (\frac{f_{APB\_CLK}}{2^{res\_bits}} MOD 1000\right )\]

The \(f_{APB\_CLK}\) here is 80 MHz, and \(res\_bits\) is the number of PWM resolution bits. When the resolution is LEDC_TIMER_10_BIT, the PWM frequency is 78 KHz. As we all know, a higher PWM frequency and resolution can better reproduce the audio signal. However, this formula shows that increasing PWM frequency means lower resolution and increasing resolution means lower PWM frequency. Thus, please adjust these parameters according to the actual application scenario to achieve a good balance.

Application Example

pwm_audio_config_t pac;
pac.duty_resolution    = LEDC_TIMER_10_BIT;
pac.gpio_num_left      = LEFT_CHANNEL_GPIO;
pac.ledc_channel_left  = LEDC_CHANNEL_0;
pac.gpio_num_right     = RIGHT_CHANNEL_GPIO;
pac.ledc_channel_right = LEDC_CHANNEL_1;
pac.ledc_timer_sel     = LEDC_TIMER_0;
pac.tg_num             = TIMER_GROUP_0;
pac.timer_num          = TIMER_0;
pac.ringbuf_len        = 1024 * 8;

pwm_audio_init(&pac));             /**< Initialize pwm audio */
pwm_audio_set_param(48000, 8, 2);  /**< Set sample rate, bits and channel number */
pwm_audio_start();                 /**< Start to run */

while(1) {

    //< Prepare audio data, such as decode mp3/wav file

    /**< Write data to play */
    pwm_audio_write(audio_data, length, &written, 1000 / portTICK_PERIOD_MS);
}

API Reference

Header File

Functions

esp_err_t pwm_audio_init(const pwm_audio_config_t *cfg)

Initializes and configure the pwm audio.

Parameters

cfg – configurations - see pwm_audio_config_t struct

Returns

  • ESP_OK Success

  • ESP_FAIL timer_group or ledc initialize failed

  • ESP_ERR_INVALID_ARG if argument wrong

  • ESP_ERR_INVALID_STATE The pwm audio already configure

  • ESP_ERR_NO_MEM Memory allocate failed

esp_err_t pwm_audio_deinit(void)

Deinitialize LEDC timer_group and output gpio.

Returns

  • ESP_OK Success

  • ESP_FAIL pwm_audio not initialized

esp_err_t pwm_audio_start(void)

Start to run pwm audio.

Returns

  • ESP_OK Success

  • ESP_ERR_INVALID_STATE pwm_audio not initialized or it already running

esp_err_t pwm_audio_stop(void)

Stop pwm audio.

Attention

Only stop timer, and the pwm will keep to output. If you want to stop pwm output, call pwm_audio_deinit function

Returns

  • ESP_OK Success

  • ESP_ERR_INVALID_STATE pwm_audio not initialized or it already idle

esp_err_t pwm_audio_write(uint8_t *inbuf, size_t len, size_t *bytes_written, TickType_t ticks_to_wait)

Write data to play.

Parameters
  • inbuf – Pointer source data to write

  • len – length of data in bytes

  • bytes_written[out] Number of bytes written, if timeout, the result will be less than the size passed in.

  • ticks_to_wait – TX buffer wait timeout in RTOS ticks. If this many ticks pass without space becoming available in the DMA transmit buffer, then the function will return (note that if the data is written to the DMA buffer in pieces, the overall operation may still take longer than this timeout.) Pass portMAX_DELAY for no timeout.

Returns

  • ESP_OK Success

  • ESP_FAIL Write encounter error

  • ESP_ERR_INVALID_ARG Parameter error

esp_err_t pwm_audio_set_param(int rate, ledc_timer_bit_t bits, int ch)

Set audio parameter, Similar to pwm_audio_set_sample_rate(), but also sets bit width.

Attention

After start pwm audio, it can’t modify parameters by call function pwm_audio_set_param. If you want to change the parameters, must stop pwm audio by call function pwm_audio_stop.

Parameters
  • rate – sample rate (ex: 8000, 44100…)

  • bits – bit width

  • ch – channel number

Returns

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

esp_err_t pwm_audio_set_sample_rate(int rate)

Set sample rate.

Parameters

rate – sample rate (ex: 8000, 44100…)

Returns

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

esp_err_t pwm_audio_set_volume(int8_t volume)

Set volume for pwm audio.

Attention

when the volume is too small, it will produce serious distortion

Parameters

volume – Volume to set (-16 ~ 16). Set to 0 for original output; Set to less then 0 for attenuation, and -16 is mute; Set to more than 0 for enlarge, and 16 is double output

Returns

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

esp_err_t pwm_audio_get_volume(int8_t *volume)

Get current volume.

Parameters

volume – Pointer to volume

Returns

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

esp_err_t pwm_audio_get_param(int *rate, int *bits, int *ch)

Get parameter for pwm audio.

Parameters
  • rate – sample rate, if you don’t care about this parameter, set it to NULL

  • bits – bit width, if you don’t care about this parameter, set it to NULL

  • ch – channel number, if you don’t care about this parameter, set it to NULL

Returns

  • Always return ESP_OK

esp_err_t pwm_audio_get_status(pwm_audio_status_t *status)

get pwm audio status

Parameters

status – current pwm_audio status

Returns

  • ESP_OK Success

Structures

struct pwm_audio_config_t

Configuration parameters for pwm_audio_init function.

Public Members

int gpio_num_left

the LEDC output gpio_num, Left channel

int gpio_num_right

the LEDC output gpio_num, Right channel

ledc_channel_t ledc_channel_left

LEDC channel (0 - 7), Corresponding to left channel

ledc_channel_t ledc_channel_right

LEDC channel (0 - 7), Corresponding to right channel

ledc_timer_t ledc_timer_sel

Select the timer source of channel (0 - 3)

ledc_timer_bit_t duty_resolution

ledc pwm bits

uint32_t ringbuf_len

ringbuffer size

Enumerations

enum pwm_audio_status_t

pwm audio status

Values:

enumerator PWM_AUDIO_STATUS_UN_INIT

pwm audio uninitialized

enumerator PWM_AUDIO_STATUS_IDLE

pwm audio idle

enumerator PWM_AUDIO_STATUS_BUSY

pwm audio busy

enum pwm_audio_channel_t

pwm audio channel define

Values:

enumerator PWM_AUDIO_CH_MONO

1 channel (mono)

enumerator PWM_AUDIO_CH_STEREO

2 channel (stereo)

enumerator PWM_AUDIO_CH_MAX