ESP32使用I2S及I2S输出到DAC(Arduino)

介绍

本文展示了在Arduino环境下ESP32的I2S的使用。
ESP32有两个I2S:I2S0和I2S1。其中I2S0支持的功能要多些。
ESP32的Arduino库似乎未提供对I2S的直接支持,因此需要调用IDF库的内容,我们需要:

#include <driver/i2s.h>

这个头文件位于Arduino放开发板包的文件夹里的:\esp32\hardware\esp32\1.0.6\tools\sdk\include\driver\driver,其中1.0.6是版本,我这里的可能偏旧一点,可能和新版本在编程细节上略有不同,比如某些枚举值的名称,但具体内容仔细查阅这个头文件即可。

硬件

I2S需要外接对应的硬件,常见的是两种:输出数据到播放器或从麦克风读取数据。这里使用的是前者,外部功放是MAX98375A,关于这个可以参见这个文章:ESP32-IDF使用I2S驱动MAX98375–解析WAV文件,该文章也讲解了I2S,而且上文提到的不同版本的编程细节不同在这里也有所体现,该文的代码写法和我下文的写法有所不同,可以自行对比尝试。
根据ESP32技术参考手册所述,I2S标准总线定义了三种信号:时钟信号BCK、声道选择信号WS和串行数据信号SD,这三个信号线都是可以正常利用IO_MUX进行引脚映射的,下文代码会体现。此外还有一个I2Sn_CLK,该时钟信号不能随便引脚映射,不过要注意它和BCK(有时也叫BCLK)不是一个东西,I2Sn_CLK不常用,所以这里可以不管。

I2S代码

#include <driver/i2s.h>
//这里可以修改引脚映射
#define BCLK  26//时钟信号
#define LRC   27//声道选择信号
#define DIN   33//串行数据信号(这里是对外部功放来说是DIN)

void setup(){
  i2s_config_t i2s_config;// I2S配置结构体
  i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);// 使用主模式并设置为发送数据
  i2s_config.sample_rate = 44100;// 设置采样率为44100Hz
  i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;// 设置数据位数为16位
  i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;// 只使用右声道
  i2s_config.communication_format = I2S_COMM_FORMAT_I2S;// I2S通信格式
  i2s_config.dma_buf_count = 8;// 设置DMA缓冲区数量为8
  i2s_config.dma_buf_len = 64;// 每个DMA缓冲区的长度为64字节
  i2s_config.intr_alloc_flags = ESP_INTR_FLAG_EDGE;// 分配中断标志
  
  i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);// I2S配置

  i2s_pin_config_t pin_config;
  pin_config.bck_io_num = BCLK;// BCLK引脚号
  pin_config.ws_io_num = LRC;// LRC引脚号
  pin_config.data_out_num = DIN;// DATA_OUT引脚号
  pin_config.data_in_num = -1;// DATA_IN引脚号(-1为没有)
  
  i2s_set_pin(I2S_NUM_0, &pin_config);// 设置引脚
}

void loop(){
  while(1){
  	uint32_t i2sWriteLen = 0;
    uint16_t i2sBuf[100];
    for(uint32_t i = 0; i < 100; i++){//441Hz锯齿波
      i2sBuf[i] = i * 10;
    }
    i2s_write(I2S_NUM_0, i2sBuf, 100*2, &i2sWriteLen, 1000);
  }
}

初始化时有一点值得注意,这里用I2S_BITS_PER_SAMPLE_16BIT来设置16位数据,是没问题的,但是,虽然头文件里定义了I2S_BITS_PER_SAMPLE_8BIT来让用户设定为8位数据,但是实际使用时ESP32会打印一个"I2S: Invalid bits per sample"的错误,据乐鑫方面说是出于某些原因在软件层面不允许8位数据,其实硬件上是可以的,怎么能避开这个限制我暂不清楚。
i2s_write 函数是用于向DMA缓存写入数据,之后芯片会自动按照设定的采样率完成数据的发送,该函数原型为:

i2s_write(i2s_port_t i2s_num, const void *src, size_t size, size_t *bytes_written, TickType_t ticks_to_wait);

i2s_num是选择两个I2S外设中的哪一个,src是传入的数据数组,size是写入数据的长度,bytes_written是成功写入的数据的长度,ticks_to_wait是超时设置。因为I2S发数据是按设置的频率发的,所以假如一次写入的数据很多而超时设置又短,就可能造成未能全部写入,此时查看bytes_written就能知道有多少数据成功写入了。据头文件中所说,若ticks_to_wait设置为portMAX_DELAY,则不会timeout。

I2S输出到DAC

如果想单独直接用DAC,可参考我的另一篇文章:ESP32使用DAC(Arduino)
ESP-IDF编程指南DAC章节所说:在 ESP32 上,DAC 的数字控制器可以在内部连接到 I2S0,并借用其 DMA 进行连续转换。虽然 DAC 转换仅需 8 位数据,但它必须是左移的 8 位(即 16 位中的高 8 位),以满足 I2S 通信格式。
在英文版本的ESP-IDF编程指南中提供了一个例程:Configuring I2S to use internal DAC for analog output,下面的代码参考了该例程,注意该代码中communication_format与I2S代码中不同,这是易忽略的地方。

#include <driver/i2s.h>

void setup(){
  i2s_config_t i2s_config;// I2S配置结构体
  i2s_config.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_DAC_BUILT_IN);// 使用主模式并设置为发送数据到DAC
  i2s_config.sample_rate = 44100;// 设置采样率为44100Hz
  i2s_config.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT;// 设置数据位数为16位
  i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;// 只使用右声道
  i2s_config.communication_format = I2S_COMM_FORMAT_I2S_MSB;// I2S通信格式MSB
  i2s_config.dma_buf_count = 8;// 设置DMA缓冲区数量为8
  i2s_config.dma_buf_len = 64;// 每个DMA缓冲区的长度为64字节
  i2s_config.intr_alloc_flags = ESP_INTR_FLAG_EDGE;// 分配中断标志
  
  i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL);// I2S配置
  
  i2s_dac_mode_t i2s_dac_mode = I2S_DAC_CHANNEL_RIGHT_EN;//使用右声道(DAC1-PIN25)
  i2s_set_dac_mode(i2s_dac_mode);//使能选择的I2S-DAC声道
  //i2s_set_pin(I2S_NUM_0, NULL);//若使用这个函数则同时使能两个声道
}

void loop(){
  while(1){
  	uint32_t i2sWriteLen = 0;
    uint16_t i2sBuf[100];
    for(uint32_t i = 0; i < 100; i++){//441Hz锯齿波
      i2sBuf[i] = i * 661;//DAC实际只使用高8位
    }
    i2s_write(I2S_NUM_0, i2sBuf, 100*2, &i2sWriteLen, 1000);
  }
}

使用I2S输出到DAC的功能在使用DAC播放音频时十分有用。此外,与输出到DAC相反地,I2S还可以从ADC自动读取数据,不过本文暂不涉及。

以下是一个使用ESP32I2S的简单示例代码,基于Arduino框架: ```cpp #include <Arduino.h> #include <driver/i2s.h> // I2S配置 #define I2S_NUM I2S_NUM_0 #define I2S_READ_LEN 1024 void setup() { Serial.begin(115200); // 配置I2S i2s_config_t i2s_config = { .mode = I2S_MODE_MASTER | I2S_MODE_RX, .sample_rate = 44100, .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT, .communication_format = I2S_COMM_FORMAT_I2S_MSB, .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1, .dma_buf_count = 8, .dma_buf_len = 64, .use_apll = false }; i2s_pin_config_t pin_config = { .bck_io_num = 26, .ws_io_num = 25, .data_out_num = I2S_PIN_NO_CHANGE, .data_in_num = 22 }; i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL); i2s_set_pin(I2S_NUM, &pin_config); } void loop() { size_t bytes_read; int16_t i2s_read_buff\[I2S_READ_LEN\]; // 从I2S读取数据 i2s_read(I2S_NUM, i2s_read_buff, sizeof(i2s_read_buff), &bytes_read, portMAX_DELAY); // 处理读取到的数据 for (int i = 0; i < bytes_read / sizeof(int16_t); i++) { int16_t sample = i2s_read_buff\[i\]; // 在这里进行你的处理,比如输出到串口或者其他操作 Serial.println(sample); } delay(1000); } ``` 这个例子演示了如何使用ESP32I2S接口进行音频数据的采集和处理。在`setup()`函数中,我们配置了I2S接口的参数,包括采样率、采样位数和引脚配置。在`loop()`函数中,我们循环读取I2S接口的数据,并进行处理。你可以根据自己的需求修改处理部分的代码。 请注意,这只是一个简单的示例代码,你可能需要根据你的具体应用场景进行适当的修改和调整。 #### 引用[.reference_title] - *1* *2* [ESP32使用I2S控制ADC和DAC](https://blog.csdn.net/tian_milk/article/details/123483124)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [esp32cam micropython使用I2S驱动DAC模块播放音频](https://blog.csdn.net/qq_33130395/article/details/120117741)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值