当前位置:   article > 正文

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

esp32 i2s

介绍

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

#include <driver/i2s.h>
  • 1

这个头文件位于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);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

初始化时有一点值得注意,这里用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);
  • 1

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);
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/人工智能uu/article/detail/976415
推荐阅读
相关标签
  

闽ICP备14008679号