#include "adc_pdm_audio_codec.h" #include #include #include #include #include #include "adc_mic.h" #include "driver/i2s_pdm.h" #include "soc/gpio_sig_map.h" #include "soc/io_mux_reg.h" #include "hal/rtc_io_hal.h" #include "hal/gpio_ll.h" #include "settings.h" #include "config.h" static const char TAG[] = "AdcPdmAudioCodec"; #define BSP_I2S_GPIO_CFG(_dout) \ { \ .clk = GPIO_NUM_NC, \ .dout = _dout, \ .invert_flags = { \ .clk_inv = false, \ }, \ } /** * @brief Mono Duplex I2S configuration structure * * This configuration is used by default in bsp_audio_init() */ #define BSP_I2S_DUPLEX_MONO_CFG(_sample_rate, _dout) \ { \ .clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG(_sample_rate), \ .slot_cfg = I2S_PDM_TX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO), \ .gpio_cfg = BSP_I2S_GPIO_CFG(_dout), \ } AdcPdmAudioCodec::AdcPdmAudioCodec(int input_sample_rate, int output_sample_rate, uint32_t adc_mic_channel, gpio_num_t pdm_speak_p,gpio_num_t pdm_speak_n, gpio_num_t pa_ctl) { input_reference_ = false; input_sample_rate_ = input_sample_rate; output_sample_rate_ = output_sample_rate; uint8_t adc_channel[1] = {0}; adc_channel[0] = adc_mic_channel; audio_codec_adc_cfg_t cfg = { .handle = NULL, .max_store_buf_size = 1024 * 2, .conv_frame_size = 1024, .unit_id = ADC_UNIT_1, .adc_channel_list = adc_channel, .adc_channel_num = sizeof(adc_channel) / sizeof(adc_channel[0]), .sample_rate_hz = (uint32_t)input_sample_rate, }; const audio_codec_data_if_t *adc_if = audio_codec_new_adc_data(&cfg); esp_codec_dev_cfg_t codec_dev_cfg = { .dev_type = ESP_CODEC_DEV_TYPE_IN, .data_if = adc_if, }; input_dev_ = esp_codec_dev_new(&codec_dev_cfg); if (!input_dev_) { ESP_LOGE(TAG, "Failed to create codec device"); return; } i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0, I2S_ROLE_MASTER); chan_cfg.auto_clear = true; // Auto clear the legacy data in the DMA buffer ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle_, NULL)); i2s_pdm_tx_config_t pdm_cfg_default = BSP_I2S_DUPLEX_MONO_CFG((uint32_t)output_sample_rate, pdm_speak_p); pdm_cfg_default.clk_cfg.up_sample_fs = AUDIO_PDM_UPSAMPLE_FS; pdm_cfg_default.slot_cfg.sd_scale = I2S_PDM_SIG_SCALING_MUL_4; pdm_cfg_default.slot_cfg.hp_scale = I2S_PDM_SIG_SCALING_MUL_4; pdm_cfg_default.slot_cfg.lp_scale = I2S_PDM_SIG_SCALING_MUL_4; pdm_cfg_default.slot_cfg.sinc_scale = I2S_PDM_SIG_SCALING_MUL_4; const i2s_pdm_tx_config_t *p_i2s_cfg = &pdm_cfg_default; ESP_ERROR_CHECK(i2s_channel_init_pdm_tx_mode(tx_handle_, p_i2s_cfg)); audio_codec_i2s_cfg_t i2s_cfg = { .port = I2S_NUM_0, .rx_handle = NULL, .tx_handle = tx_handle_, }; const audio_codec_data_if_t *i2s_data_if = audio_codec_new_i2s_data(&i2s_cfg); codec_dev_cfg.dev_type = ESP_CODEC_DEV_TYPE_OUT; codec_dev_cfg.codec_if = NULL; codec_dev_cfg.data_if = i2s_data_if; output_dev_ = esp_codec_dev_new(&codec_dev_cfg); output_volume_ = 100; if(pa_ctl != GPIO_NUM_NC) { pa_ctrl_pin_ = pa_ctl; gpio_config_t io_conf = {}; io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.mode = GPIO_MODE_OUTPUT; io_conf.pin_bit_mask = (1ULL << pa_ctrl_pin_); io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; io_conf.pull_up_en = GPIO_PULLUP_DISABLE; gpio_config(&io_conf); } gpio_set_drive_capability(pdm_speak_p, GPIO_DRIVE_CAP_0); if(pdm_speak_n != GPIO_NUM_NC){ PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[pdm_speak_n], PIN_FUNC_GPIO); gpio_set_direction(pdm_speak_n, GPIO_MODE_OUTPUT); esp_rom_gpio_connect_out_signal(pdm_speak_n, I2SO_SD_OUT_IDX, 1, 0); //反转输出 SD OUT 信号 gpio_set_drive_capability(pdm_speak_n, GPIO_DRIVE_CAP_0); } // 初始化输出定时器 esp_timer_create_args_t output_timer_args = { .callback = &AdcPdmAudioCodec::OutputTimerCallback, .arg = this, .dispatch_method = ESP_TIMER_TASK, .name = "output_timer" }; ESP_ERROR_CHECK(esp_timer_create(&output_timer_args, &output_timer_)); ESP_LOGI(TAG, "AdcPdmAudioCodec initialized"); } AdcPdmAudioCodec::~AdcPdmAudioCodec() { // 删除定时器 if (output_timer_) { esp_timer_stop(output_timer_); esp_timer_delete(output_timer_); output_timer_ = nullptr; } ESP_ERROR_CHECK(esp_codec_dev_close(output_dev_)); esp_codec_dev_delete(output_dev_); ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_)); esp_codec_dev_delete(input_dev_); } void AdcPdmAudioCodec::SetOutputVolume(int volume) { ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(output_dev_, volume)); AudioCodec::SetOutputVolume(volume); } void AdcPdmAudioCodec::EnableInput(bool enable) { if (enable == input_enabled_) { return; } if (enable) { esp_codec_dev_sample_info_t fs = { .bits_per_sample = 16, .channel = 1, .channel_mask = ESP_CODEC_DEV_MAKE_CHANNEL_MASK(0), .sample_rate = (uint32_t)input_sample_rate_, .mclk_multiple = 0, }; ESP_ERROR_CHECK(esp_codec_dev_open(input_dev_, &fs)); } else { ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_)); } AudioCodec::EnableInput(enable); } void AdcPdmAudioCodec::EnableOutput(bool enable) { if (enable == output_enabled_) { return; } if (enable) { // Play 16bit 1 channel esp_codec_dev_sample_info_t fs = { .bits_per_sample = 16, .channel = 1, .channel_mask = 0, .sample_rate = (uint32_t)output_sample_rate_, .mclk_multiple = 0, }; ESP_ERROR_CHECK(esp_codec_dev_open(output_dev_, &fs)); ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(output_dev_, output_volume_)); // 强制按板卡配置重配PDM TX时钟,覆盖第三方库在set_fmt中的默认up_sample_fs // 若通道已启用,先禁用再重配,最后再启用 ESP_ERROR_CHECK_WITHOUT_ABORT(i2s_channel_disable(tx_handle_)); i2s_pdm_tx_clk_config_t clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG((uint32_t)output_sample_rate_); clk_cfg.up_sample_fs = AUDIO_PDM_UPSAMPLE_FS; ESP_ERROR_CHECK(i2s_channel_reconfig_pdm_tx_clock(tx_handle_, &clk_cfg)); ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_)); if(pa_ctrl_pin_ != GPIO_NUM_NC){ gpio_set_level(pa_ctrl_pin_, 1); } // 启用输出时启动定时器 if (output_timer_) { esp_timer_start_once(output_timer_, TIMER_TIMEOUT_US); } } else { // 禁用输出时停止定时器 if (output_timer_) { esp_timer_stop(output_timer_); } if(pa_ctrl_pin_ != GPIO_NUM_NC){ gpio_set_level(pa_ctrl_pin_, 0); } ESP_ERROR_CHECK(esp_codec_dev_close(output_dev_)); } AudioCodec::EnableOutput(enable); } int AdcPdmAudioCodec::Read(int16_t* dest, int samples) { if (input_enabled_) { ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_read(input_dev_, (void*)dest, samples * sizeof(int16_t))); } return samples; } int AdcPdmAudioCodec::Write(const int16_t* data, int samples) { if (output_enabled_) { ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_write(output_dev_, (void*)data, samples * sizeof(int16_t))); // 重置输出定时器 if (output_timer_) { esp_timer_stop(output_timer_); esp_timer_start_once(output_timer_, TIMER_TIMEOUT_US); } } return samples; } void AdcPdmAudioCodec::Start() { Settings settings("audio", false); output_volume_ = settings.GetInt("output_volume", output_volume_); if (output_volume_ <= 0) { ESP_LOGW(TAG, "Output volume value (%d) is too small, setting to default (10)", output_volume_); output_volume_ = 10; } EnableInput(true); EnableOutput(true); ESP_LOGI(TAG, "Audio codec started"); } // 定时器回调函数实现 void AdcPdmAudioCodec::OutputTimerCallback(void* arg) { AdcPdmAudioCodec* codec = static_cast(arg); if (codec && codec->output_enabled_) { codec->EnableOutput(false); } }