PWM 音频

[English]

支持的芯片

ESP32

ESP32-S2

ESP32-S3

ESP32-C3

PWM 音频功能使用 ESP32 内部的 LEDC 外设产生 PWM 播放音频,无需使用外部的音频 Codec 芯片,适用于对音质要求不高而对成本敏感的应用。

特性

  • 允许使用任意具有输出功能的 GPIO 作为音频输出管脚

  • 支持 8 ~ 10 位的 PWM 分辨率

  • 支持立体声

  • 支持 8 ~ 48 KHz 采样率

结构

../_images/pwm_audio_diagram.png

结构

  1. 首先对写入的数据进行编码调整以满足 PWM 的输入要求,包括数据的移位和偏移;

  2. 通过一个 Ring Buffer 将数据发送到 Timer Group 的 ISR (Interrupt Service Routines) 函数;

  3. Timer Group 按照设定的采样率从 Ring Buffer 读出数据并写入到 LEDC。

注解

由于输出的是 PWM 信号,需要将输出进行低通滤波后才能得到音频波形。

PWM 频率

输出 PWM 的频率不支持直接配置,需通过配置 PWM 分辨率位数来计算,计算公式如下:

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

其中 \(f_{APB\_CLK}\) 为 80 MHz,\(res\_bits\) 为 PWM 分辨率位数,当分辨率为 LEDC_TIMER_10_BIT 时,PWM 频率为 78 KHz。 更高的 PWM 频率和分辨率可以更好地还原音频信号,但是从公式可以看出,增大 PWM 频率意味着分辨率降低,提高分辨率意味着频率减小,可根据实际应用调整达到良好的平衡。

应用示例

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 numner */
pwm_audio_start();                 /**< Start to run */

while(1) {

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

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

API 参考

Header File

Functions

esp_err_t pwm_audio_init(const pwm_audio_config_t *cfg)

Initializes and configure the pwm audio.

Return

  • 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

Parameters

esp_err_t pwm_audio_deinit(void)

Deinitialize LEDC timer_group and output gpio.

Return

  • ESP_OK Success

  • ESP_FAIL pwm_audio not initialized

esp_err_t pwm_audio_start(void)

Start to run pwm audio.

Return

  • 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

Return

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

Return

  • ESP_OK Success

  • ESP_FAIL Write encounter error

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • inbuf: Pointer source data to write

  • len: length of data in bytes

  • [out] bytes_written: 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.

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.

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

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

  • bits: bit width

  • ch: channel number

esp_err_t pwm_audio_set_sample_rate(int rate)

Set sample rate.

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

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

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

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

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

esp_err_t pwm_audio_get_volume(int8_t *volume)

Get current volume.

Return

  • ESP_OK Success

  • ESP_ERR_INVALID_ARG Parameter error

Parameters
  • volume: Pointer to volume

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

Get parameter for pwm audio.

Return

  • Always return ESP_OK

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

esp_err_t pwm_audio_get_status(pwm_audio_status_t *status)

get pwm audio status

Return

  • ESP_OK Success

Parameters
  • status: current pwm_audio status

Structures

struct pwm_audio_config_t

Configuration parameters for pwm_audio_init function.

Public Members

timer_group_t tg_num

timer group number (0 - 1)

timer_idx_t timer_num

timer number (0 - 1)

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:

PWM_AUDIO_STATUS_UN_INIT = 0

pwm audio uninitialized

PWM_AUDIO_STATUS_IDLE = 1

pwm audio idle

PWM_AUDIO_STATUS_BUSY = 2

pwm audio busy

enum pwm_audio_channel_t

pwm audio channel define

Values:

PWM_AUDIO_CH_MONO = 0

1 channel (mono)

PWM_AUDIO_CH_STEREO = 1

2 channel (stereo)

PWM_AUDIO_CH_MAX