近一段时间以来,手机、计算机和家庭自动化产品中的数字系统及其音频数据需求发生了巨大变化,其中就包括来自处理器的音频信号正在数字化。而不同系统中的这些数据通过许多设备进行处理,如DSP、ADC、DAC、数字I/O接口等。
为了使这些设备能够相互通信音频数据,需要一个标准协议,而I2S协议就是其中之一。I2S是一个串行总线接口,由飞利浦半导体于1986年2月设计,用于设备之间的数字音频接口。
用于将数字音频数据从一个设备传输到另一个设备的协议称为I2S(Inter-IC Sound)协议,简写I²S。该协议将PCM(脉冲编码调制)音频数据从电子设备中的一个IC传输到另一个IC。
I2S在将预先录制的音频文件从MCU传输到DAC或放大器方面起着关键作用。I2S协议还可用于使用麦克风对音频进行数字化。另外,由于I2S协议中没有压缩功能,所以不能播放OGG、MP3或其它压缩音频的音频格式,但是,可以播放WAV文件。
I2S协议功能包括以下几方面内容:
I2S通信协议是一种 3线协议,可通过3线串行总线简单地处理音频数据,其中包括SCK(连续串行时钟)、WS(字选择)和SD(串行数据)。
1、SCK
SCK是I2S协议的第一行,也称为BCLK(位时钟线),用于在类似的周期上获取数据。串行时钟频率可以使用公式简单定义:频率=采样率x每个通道的位数x通道数。
2、WS
在I2S通信协议中,WS是分隔右声道或左声道的线,也称为FS(帧选择)线。
3、SD
SD是有效负载在2个补码内传输的最后一条线。因此,首先传输MSB非常重要,因为发送器和接收器可能包含不同的字长。因此,发送器或接收器必须识别发送了多少比特。
发送器可以在时钟脉冲的前沿或后沿发送数据,这可以在相应的控制寄存器中配置。但接收器仅在时钟脉冲的前沿锁存串行数据和WS。发送器仅在WS改变后的一个时钟脉冲后发送数据。接收器使用WS信号同步串行数据。
当多个I2S组件相互连接时,这称为I2S网络,该网络的组件包括不同的名称和不同的功能,如下图显示了3个不同的网络。这里使用ESP NodeMCU板作为发射器,使用I2S音频分线板作为接收器。用于连接发射器和接收器的三根线是SCK、WS和SD。
在上述所有I2S网络中,只有一个主设备可用,还有许多其他组件可以传输或接收声音数据。此外,在I2S中,任何设备都可以通过提供时钟信号成为主机。
为了更好地理解I2S及其功能,这里提供了如下所示的I2S通信协议时序图。I2S协议的时序图如下所示,包括三根线SCK、WS和SD。
在上图中,首先,串行时钟的频率=采样率*每个通道的位数 * 通道数。WS线是第二行,它在右声道的“1”和左声道的“0”之间变化。第三条线是串行数据线,数据在下降沿的每个时钟周期传输,用点从高到低表示。
此外,可以注意到WS线在传输MSB之前改变一个CLK周期,这为接收器提供了时间来存储较早的字并清除下一个字的输入寄存器。当WS改变后SCK改变时发送 MSB。
每当在发射器和接收器之间传输数据时,都会有传播延迟,传播延迟=(外部时钟与接收器内部时钟之间的时间差)+(内部时钟与接收数据时之间的时间差)。
为了最小化传播延迟并同步发送器和接收器之间的数据传输,要求发送器的时钟周期为T >tr。这里假设T是发送器的时钟周期,tr是发送器的最小时钟周期。
在上述条件下,如果考虑例如数据传输速率为2.5MHz的发射器,则有:tr=360纳秒
接收器作为从机,数据传输速率为 2.5MHz,则有:
该项目的主要目标是使用Arduino I2S库制作I2S Theremin接口。制作这个项目所需的组件是Arduino MKR Zero、面包板、跳线、MAX98357A、3W、4欧姆扬声器和RobotGeek滑块。
Arduino I2S库仅允许通过I2S总线传输和接收数字音频数据。因此,此示例旨在说明如何利用此库来驱动I2S DAC,以再现Arduino设计中计算的声音。
该电路可以连接为,本示例中使用的I2S DAC仅需要三根电线以及一个用于I2S总线的电源。Arduino MKRZero上I2S的连接如下:
工作原理
基本上,Theremin有两个控制,分别是音高和音量。因此,这两个参数是通过移动两个滑动电位器来修改的,但是,也可以调整它们来读取它们。两个电位器以分压器的形式连接,因此移动这些电位器将获得0到1023之间的值。之后,这些值映射在最大和最小频率以及最小和最大音量之间。
I2S总线上传输的声音是一个简单的正弦波,其振幅和频率根据电位器的读数进行修改。
下面给出了将Theremin与Arduino MKRZero、2滑块电位器和I2S DAC接口的代码,代码如下:
#include <I2S.h>
const int maxFrequency = 5000; //maximum generated frequency
const int minFrequency = 220; //minimum generated frequency
const int maxVolume = 100; //max volume of the generated frequency
const int minVolume = 0; //min volume of the generated frequency
const int sampleRate = 44100; //samplerate of the generated frequency
const int wavSize = 256; //buffer size
short sine[wavSize]; //buffer in which the sine values are stored
const int frequencyPin = A0; //pin connected to the pot which determines the frequency of the signal
const int amplitudePin = A1; //pin connected to the pot which determines the amplitude of the signal
const int button = 6; //pin connected to the button control to display the frequency
void setup()
{
Serial.begin(9600); //configue the serial port
// Initialize the I2S transmitter.
if (!I2S.begin(I2S_PHILIPS_MODE, sampleRate, 16)) {
Serial.println(“Failed to initialize I2S!”);
while (1);
}
generateSine(); // fill buffer with sine values
pinMode(button, INPUT_PULLUP); //put the button pin in input pullup
}
void loop() {
if (digitalRead(button) == LOW)
{
float frequency = map(analogRead(frequencyPin), 0, 1023, minFrequency, maxFrequency); //map frequency
int amplitude = map(analogRead(amplitudePin), 0, 1023, minVolume, maxVolume); //map amplitude
playWave(frequency, 0.1, amplitude); //play sound
//print values on serial
Serial.print(“Frequency = “);
Serial.println(frequency);
Serial.print(“Amplitude = “);
Serial.println(amplitude);
}
}
void generateSine() {
for (int i = 0; i < wavSize; ++i) {
sine[i] = ushort(float(100) * sin(2.0 * PI * (1.0 / wavSize) * i)); //100 is used to not have small numbers
}
}
void playWave(float frequency, float seconds, int amplitude) {
// Play back the provided waveform buffer for the specified
// amount of seconds.
// First calculate how many samples need to play back to run
// for the desired amount of seconds.
unsigned int iterations = seconds * sampleRate;
// Then calculate the ‘speed’ at which we move through the wave
// buffer based on the frequency of the tone being played.
float delta = (frequency * wavSize) / float(sampleRate);
// Now loop through all the samples and play them, calculating the
// position within the wave buffer for each moment in time.
for (unsigned int i = 0; i < iterations; ++i) {
short pos = (unsigned int)(i * delta) % wavSize;
short sample = amplitude * sine[pos];
// Duplicate the sample so it’s sent to both the left and right channels.
// It appears the order is the right channel, left channel if you want to write
// stereo sound.
while (I2S.availableForWrite() < 2);
I2S.write(sample);
I2S.write(sample);
}
}
I2C和I2S协议之间的区别包括以下几方面内容:
| I2C | I2S |
| I2C协议代表 inter-IC总线协议 | I2S代表Inter-IC Sound协议。 |
| 主要用于在放置在类似PCB上的集成电路之间运行信号。 | 主要用于连接数字音频设备。 |
| 它在几个主机和从机之间使用两条线,如SDA和SCL 。 | 它使用三个线路WS、SCK和SD。 |
| 支持多主多从。 | 支持单个主控。 |
| 该协议支持CLK扩展。 | 该协议没有CLK扩展。 |
| I2C包括额外的起始位和停止位。 | I2S不包括任何起始位和停止位。 |
I2S总线的优点包括以下几方面内容:
I2S总线的缺点包括以下几方面内容:
I2S协议的应用包括以下几方面内容:
I2S是一种数字音频接口标准,通常用于在音频设备之间传输音频数据。它是一种串行协议,允许将音频数据以数字形式从一个设备传输到另一个设备,同时保持高质量和高速度。
I2S标准包含三条线路:数据线、时钟线和帧同步线。数据线用于传输音频数据,时钟线用于同步数据传输速度,帧同步线用于确定音频数据的帧界限和起始位置。I2S还可以通过控制线实现其他功能,如音频设备的启动和停止。
I2S可以传输不同位深度和采样率的音频数据,通常支持16位或24位深度和44.1 kHz或48 kHz的采样率。它也可以传输多通道音频数据,包括立体声、5.1声道和7.1声道等。I2S广泛用于许多消费电子产品中,如智能手机、平板电脑、音频播放器和电视机等。它也被用于专业音频设备,如数字音频工作站、数字调音台和数字信号处理器等。