小智ai,esp32

MIC : microphone : 麦克风,话筒

把外界说话,环境声音这种模拟声波转换成电信号,是音频采集输入设备

声音 ---》 mic采集声波 ---》 转换为数字音频(PDM自带模数转换) ---》 i2s总线送入esp32 ---》 完成录音

PDM : 数字麦 INMP441

speaker : 扬声器,喇叭,,音频输出设备,,,把芯片输出的数字电信号,还原成能听见的声波

i2s : inter-ic sound : 集成电路内置音频总线,,, i two s

专门用来传输数字音频信号的通信协议,,只传声音数据,不做控制指令,,,解决芯片和麦克风或功放之间的音频传输

c 复制代码
#include <stdio.h>

#include "driver/i2s_common.h"
#include "driver/i2s_std.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

// 共用时钟
#define I2S_BCLK 5
#define I2S_WS 6

// max98357
#define I2S_SPK_DOUT 7

// INMP441 输入
#define I2S_MIC_DIN 8


#define audio_buf_len 1024

#define SAMPLE_RATE 16000

// 播放通道
static i2s_chan_handle_t tx_handle = NULL;
// 录音通道
static i2s_chan_handle_t rx_handle = NULL;

esp_err_t ret;
void i2s_init(void) {

    i2s_chan_config_t chan_config = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0,I2S_ROLE_MASTER);

     ret = i2s_new_channel(&chan_config,&tx_handle,&rx_handle);
    printf("new_channel=%d\n", ret);

    // ESP_ERROR_CHECK();
}

// 初始化max98357
void speaker_init(void) {
    i2s_std_config_t tx_config = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED, // master clk,,  外界立体codec声卡时,必须分配一个gpio输出MCK
            .bclk = I2S_BCLK, // 时钟线,, 一个周期传一个 音频单位
            .ws = I2S_WS, // write select
            .dout = I2S_SPK_DOUT,
            .din = I2S_GPIO_UNUSED,
            .invert_flags = {// 硬件布线 / 电平相位错位  需要反转
                .mclk_inv = false,// MCK主时钟是否反转
                .bclk_inv = false,
                .ws_inv = false,
            }


        }
    };


    tx_config.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;// 左声道


    // ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle,&tx_config));
    ret = i2s_channel_init_std_mode(tx_handle, &tx_config);
    printf("tx_init=%d\n", ret);

    // ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
    ret = i2s_channel_enable(tx_handle);
    printf("tx_enable=%d\n", ret);
}

/**
 * INMP441原生输出 24bit,,  如果配置16bit,,只会取其中16bit,,音量特别小,全是噪音,数据错位,,一般用32Bit
 */
void microphone_init(void) {
    i2s_std_config_t rx_config = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
        .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,
            .bclk = I2S_BCLK,
            .ws = I2S_WS,
            .dout = I2S_GPIO_UNUSED,
            .din = I2S_MIC_DIN,
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv = false
            }
        }
    };

    rx_config.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;// 左声道




    // ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle,&rx_config));

    ret = i2s_channel_init_std_mode(rx_handle, &rx_config);
    printf("rx_init=%d\n", ret);
    // ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
    ret = i2s_channel_enable(rx_handle);
    printf("rx_enable=%d\n", ret);

}


size_t audio_read(void *buf,size_t len) {

    size_t bytes_read;

    /**
     *@param2 : 存储读取到的PCM
     *@param3 : 缓冲区总字节数
     *@param4 : 本次实际读取字节数
     **/
    i2s_channel_read(rx_handle,buf,len,&bytes_read,portMAX_DELAY);


    return bytes_read;

}


void audio_write(void *buf,size_t len) {
    size_t bytes_write;
    i2s_channel_write(tx_handle,buf,len,&bytes_write,portMAX_DELAY);
}


#define AUDIO_FRAME_SIZE 1024
int16_t mic_buffer[AUDIO_FRAME_SIZE];
int16_t spk_buffer[AUDIO_FRAME_SIZE];


void audio_loopback(void) {
    size_t bytes_read;
    size_t bytes_write;
    static int cnt = 0;

    while(1) {
       //  i2s_channel_read(rx_handle,mic_buffer,sizeof(mic_buffer),&bytes_read,portMAX_DELAY);
       //
       //
       //  // 读了多少个样本
       // int sample_count =  bytes_read / sizeof(int32_t);
       //
       //  for(int i=0;i<sample_count;i++) {
       //      spk_buffer[i] =
       //          (int16_t)(mic_buffer[i] >> 14);
       //  }
       //
       //  cnt++;
       //  if (cnt==100) {
       //      printf("%ld\n", mic_buffer[0]);
       //      cnt=0;
       //  }
       //
       //
       //
       //  i2s_channel_write(tx_handle,spk_buffer,sample_count * sizeof(int16_t),&bytes_write,portMAX_DELAY);





        printf("before read \n");
         ret = i2s_channel_read(rx_handle,mic_buffer,sizeof(mic_buffer),&bytes_read,portMAX_DELAY);


        // int sample_count = bytes_read / sizeof(int16_t);

        // 读多少,写多少
        i2s_channel_write(tx_handle,mic_buffer, bytes_read,&bytes_write,portMAX_DELAY);
        //
        // printf("after read ret=%d bytes=%d\n", ret, (int)bytes_read);
        // uint32_t *p=(uint32_t*)mic_buffer;


        // for(int i = 0; i < 32; i++)
        // {
        //     printf("%d\n", (int16_t)mic_buffer[i]);
        // }

        for(int i = 0; i < 32; i += 2)
        {
            printf("%6d\n", mic_buffer[i]);
        }
        printf("\n");

        vTaskDelay(pdMS_TO_TICKS(100));


    }
}

void app_main(void)
{

    i2s_init();
    speaker_init();
    microphone_init();


    printf("audio loopback start...\n");

    audio_loopback();

}

没有杂音:

c 复制代码
#include <stdio.h>

#include "driver/i2s_common.h"
#include "driver/i2s_std.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

// 共用时钟
#define I2S_BCLK 5
#define I2S_WS 6

// max98357
#define I2S_SPK_DOUT 7

// INMP441 输入
#define I2S_MIC_DIN 8


#define audio_buf_len 1024

#define SAMPLE_RATE 16000

// 播放通道
static i2s_chan_handle_t tx_handle = NULL;
// 录音通道
static i2s_chan_handle_t rx_handle = NULL;

esp_err_t ret;
void i2s_init(void) {

    i2s_chan_config_t chan_config = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_0,I2S_ROLE_MASTER);

     ret = i2s_new_channel(&chan_config,&tx_handle,&rx_handle);
    printf("new_channel=%d\n", ret);

    // ESP_ERROR_CHECK();
}

// 初始化max98357
void speaker_init(void) {
    i2s_std_config_t tx_config = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
        .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED, // master clk,,  外界立体codec声卡时,必须分配一个gpio输出MCK
            .bclk = I2S_BCLK, // 时钟线,, 一个周期传一个 音频单位
            .ws = I2S_WS, // write select
            .dout = I2S_SPK_DOUT,
            .din = I2S_GPIO_UNUSED,
            .invert_flags = {// 硬件布线 / 电平相位错位  需要反转
                .mclk_inv = false,// MCK主时钟是否反转
                .bclk_inv = false,
                .ws_inv = false,
            }


        }
    };


    tx_config.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;// 左声道


    // ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle,&tx_config));
    ret = i2s_channel_init_std_mode(tx_handle, &tx_config);
    printf("tx_init=%d\n", ret);

    // ESP_ERROR_CHECK(i2s_channel_enable(tx_handle));
    ret = i2s_channel_enable(tx_handle);
    printf("tx_enable=%d\n", ret);
}

/**
 * INMP441原生输出 24bit,,  如果配置16bit,,只会取其中16bit,,音量特别小,全是噪音,数据错位,,一般用32Bit
 */
void microphone_init(void) {
    i2s_std_config_t rx_config = {
        .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(SAMPLE_RATE),
        .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT,I2S_SLOT_MODE_MONO),
        .gpio_cfg = {
            .mclk = I2S_GPIO_UNUSED,
            .bclk = I2S_BCLK,
            .ws = I2S_WS,
            .dout = I2S_GPIO_UNUSED,
            .din = I2S_MIC_DIN,
            .invert_flags = {
                .mclk_inv = false,
                .bclk_inv = false,
                .ws_inv = false
            }
        }
    };

    rx_config.slot_cfg.slot_mask = I2S_STD_SLOT_LEFT;// 左声道




    // ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle,&rx_config));

    ret = i2s_channel_init_std_mode(rx_handle, &rx_config);
    printf("rx_init=%d\n", ret);
    // ESP_ERROR_CHECK(i2s_channel_enable(rx_handle));
    ret = i2s_channel_enable(rx_handle);
    printf("rx_enable=%d\n", ret);

}


size_t audio_read(void *buf,size_t len) {

    size_t bytes_read;

    /**
     *@param2 : 存储读取到的PCM
     *@param3 : 缓冲区总字节数
     *@param4 : 本次实际读取字节数
     **/
    i2s_channel_read(rx_handle,buf,len,&bytes_read,portMAX_DELAY);


    return bytes_read;

}


void audio_write(void *buf,size_t len) {
    size_t bytes_write;
    i2s_channel_write(tx_handle,buf,len,&bytes_write,portMAX_DELAY);
}


#define AUDIO_FRAME_SIZE 1024
int16_t mic_buffer[AUDIO_FRAME_SIZE];
int16_t spk_buffer[AUDIO_FRAME_SIZE];


void audio_loopback(void) {
    size_t bytes_read;
    size_t bytes_write;
    static int cnt = 0;

    while(1) {
       //  i2s_channel_read(rx_handle,mic_buffer,sizeof(mic_buffer),&bytes_read,portMAX_DELAY);
       //
       //
       //  // 读了多少个样本
       // int sample_count =  bytes_read / sizeof(int32_t);
       //
       //  for(int i=0;i<sample_count;i++) {
       //      spk_buffer[i] =
       //          (int16_t)(mic_buffer[i] >> 14);
       //  }
       //
       //  cnt++;
       //  if (cnt==100) {
       //      printf("%ld\n", mic_buffer[0]);
       //      cnt=0;
       //  }
       //
       //
       //
       //  i2s_channel_write(tx_handle,spk_buffer,sample_count * sizeof(int16_t),&bytes_write,portMAX_DELAY);





        printf("before read \n");
         ret = i2s_channel_read(rx_handle,mic_buffer,sizeof(mic_buffer),&bytes_read,portMAX_DELAY);


        int sample_count = bytes_read / sizeof(int16_t);

        for(int i=0;i<sample_count;i++) {
            // int32_t sample = mic_buffer[i] >> 16;
            // sample /= 8; // 衰减4-8倍
            // spk_buffer[i] = (uint16_t)sample;


            int16_t sample = mic_buffer[i];
            sample /= 8;
            spk_buffer[i] = sample;
        }


        // 读多少,写多少
        i2s_channel_write(tx_handle,spk_buffer, sample_count*sizeof(int16_t),&bytes_write,portMAX_DELAY);
        //
        // printf("after read ret=%d bytes=%d\n", ret, (int)bytes_read);
        // uint32_t *p=(uint32_t*)mic_buffer;


        // for(int i = 0; i < 32; i++)
        // {
        //     printf("%d\n", (int16_t)mic_buffer[i]);
        // }

        for(int i = 0; i < 8; i ++)
        {
            printf("%6d\n", spk_buffer[i]);
        }
        printf("\n");

        // vTaskDelay(pdMS_TO_TICKS(100));


    }
}

void app_main(void)
{

    i2s_init();
    speaker_init();
    microphone_init();


    printf("audio loopback start...\n");

    audio_loopback();

}
相关推荐
Doraemomo13 分钟前
IMX6ULL裸机开发——GPIO的使用
单片机·嵌入式硬件
笨笨饿29 分钟前
#138_解决Codex要五次回复的问题
linux·stm32·单片机·嵌入式硬件·mcu·物联网·嵌入式实时数据库
云泽8081 小时前
STM32 USART 详解(七):超时设置、非阻塞发送与 ReadLine 实现
stm32·单片机·嵌入式硬件
2401_862880822 小时前
UART 通信协议
单片机·51单片机
沐欣工作室_lvyiyi3 小时前
矿井安全监测系统设计(论文+源码)
单片机·无线通信·云平台·物联网毕业设计·矿井安全监测系统·机智云
ZLG_zhiyuan3 小时前
低空测试“升维”:ZUS系列示波器如何破解复杂信号的“测与析”难点?
单片机·嵌入式硬件
..Dauntless..4 小时前
【stm32】GPIO(下篇)——统一编址、源码理解和输入模式
stm32·单片机·嵌入式硬件
youyou-06064 小时前
AUTOSAR‑COM 常见问题 QA (1)
java·linux·服务器·单片机·汽车
白搞电子6 小时前
ESP-Mosaico 桌宠为什么会误睡?AS312 PIR 原理、休眠逻辑与排错方法
单片机·嵌入式硬件·动画
youcans_6 小时前
【嵌入式软件AI编程】08. 第一个STM32工程
stm32·单片机·ai编程·嵌入式软件·claude code