赞
踩
本文展示了在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不常用,所以这里可以不管。
#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。
如果想单独直接用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自动读取数据,不过本文暂不涉及。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。