IIS是什么?+ mpy实例

2022-04-15 15:19:22 浏览数 (1)

我今天收拾东西找到了几个IIS的传感器,看了下都是音频的器件。以前使用是ESP32 自带的IIS,因为时间的原因没有研究很多,这篇文章做下简单的总结。

I²S(Inter-IC Sound或Integrated Interchip Sound)是飞利浦公司制定的一种数字音频标准,于1986年发布,最近的一次修改是1996年。该总线适用于音频设备之间的数据传输,现已广泛应用于各种多媒体系统。它的时钟信号与音频数据流分离,与需要时钟恢复的系统相比,抖动比较低,为用户节省了购买抵抗音频抖动的专业设备的费用。

I²S总线规范

I²S至少有三条信号线:

SCK: (continuous serial clock) 串行时钟, 对应数字音频的每一位数据,SCK都有1个脉冲。SCK的频率=2×采样频率×采样位数。

WS: (word select) 字段(声道)选择,用于切换左右声道的数据。WS的频率等于采样频率。声道选择线表明了正在被传输的声道。

WS为“1”表示正在传输的是左声道的数据。

WS为“0”表示正在传输的是右声道的数据。

WS可以在串行时钟的上升沿或者下降沿发生改变,并且WS信号不需要一定是对称的。在从属设备端,WS在时钟信号的上升沿发生改变。

WS总是在最高位传输前的一个时钟周期发生改变,这样可以使从属设备得到与被传输的串行数据同步的时间,并且使接收端存储当前的命令以及为下次的命令清除空间。

SD: (serial data) 串行数据,用二进制补码表示的音频数据。I²S格式的信号无论有多少位有效数据,数据的最高位总是被最先传输(在WS变化(也就是一帧开始)后的第2个SCK脉冲处),因此最高位拥有固定的位置,而最低位的位置则是依赖于数据的有效位数。

这就使得接收端与发送端的有效位数可以不同。如果接收端能处理的有效位数少于发送端,可以放弃数据帧中多余的低位数据;如果接收端能处理的有效位数多于发送端,可以自行补足剩余的位(常补足为零)。

这种同步机制使得数字音频设备的互连更加方便,而且不会造成数据错位。为了保证数字音频信号的正确传输,发送端和接收端应该采用相同的数据格式和长度。当然,对I²S格式来说数据长度可以不同。

对于系统而言,产生SCK和WS的信号端就是主设备,用MASTER表示,简单系统示意图如下图所示:

系统示意图

此标准是Philips (后来为NXP,现在位Qualcomm)于1986年推出的,最后一个版本修订于1996年.现在是22年,大概就是26年了。

其实很简单,就是一个协议而已.现在好像是可以控制灯光了。

下面是参考文末的文章,使用ESP32 Cam实现:

代码语言:javascript复制
from machine import I2S
from machine import Pin
import time
 
# 初始化引脚定义
sck_pin = Pin(14)  # 串行时钟输出
ws_pin = Pin(13)   # 字时钟
sd_pin = Pin(12)   # 串行数据输出
 
# 初始化i2s
audio_out = I2S(1,sck=sck_pin,
            ws=ws_pin,
            sd=sd_pin,
            mode=I2S.TX,
            bits=16,
            format=I2S.MONO,
            rate=16000,
            ibuf=20000)

使用microPython就是这样

代码语言:javascript复制
sck 是串行时钟线的引脚对象
ws 是单词选择行的引脚对象
sd 是串行数据线的引脚对象
mode 指定接收或发送
bits 指定样本大小(位),16 或 32
format 指定通道格式,STEREO(左右声道) 或 MONO(单声道)
rate 指定音频采样率(样本/秒)
ibuf 指定内部缓冲区长度(字节)

这些是参数的意思

代码语言:javascript复制
from machine import I2S
from machine import Pin
import time

# 初始化引脚定义
sck_pin = Pin(14)  # 串行时钟输出
ws_pin = Pin(13)   # 字时钟
sd_pin = Pin(12)   # 串行数据输出

# 初始化i2s
audio_out = I2S(1, sck=sck_pin,
                ws=ws_pin,
                sd=sd_pin,
                mode=I2S.TX,
                bits=16,
                format=I2S.MONO,
                rate=16000,
                ibuf=20000)
wavtempfile = "temp.wav"
wav = open(wavtempfile, 'rb')

# 前进到WAV文件中数据段的第一个字节
pos = wav.seek(44)

# 分配样本数组
# 用于减少while循环中堆分配的内存视图
wav_samples = bytearray(1024)
wav_samples_mv = memoryview(wav_samples)

print('播放音频')
# 下面的0.016得来的方法:16000(采样率) x 8(采样位宽,我用的是8位音频,单位bit) x1(通道数,单声道1,立体声n) ÷ 8(1字节=8bit) ÷ 1000000(秒换算成微秒)
# 上方的参数我都是使用的固定参数,我通过音频软件得出的,最好是从wav文件头中得出所有参数,就比较准确
# 音频总时长 us(微秒)
all_time = (len(wav)-44) / 0.016
# 从WAV文件中连续读取音频样本
# 并将其写入I2S DAC
while True:
    try:
        num_read = wav.readinto(wav_samples_mv)
        num_written = 0
        # WAV文件结束
        if num_read == 0:
            break
            # 前进到数据段的第一个字节
            # pos = wav.seek(44)
        # 在首次执行的时候记录开始时间
        if num_written == 0:
            # 记录播放开始时间
            start_time = time.ticks_us()
        # 循环,直到所有样本都写入I2S外围设备
        while num_written < num_read:
            num_written  = audio_out.write(
                wav_samples_mv[num_written:num_read])

    except Exception as e:
        print('错误,', e)
        break

从SD卡中读取音频播放,之后注销:

代码语言:javascript复制
# 等待音频播放完毕
while 1:
    # 播放结束时间
    end_time = time.ticks_us()
    # 如果当前时间减去开始播放的时间大于音频时长
    if (end_time - start_time) > all_time:
        # 取消初始化 I2S 总线
        audio_out.deinit()
        # 停止等待
        break
# 播放完毕
# 关闭文件
wav.close()

以上的的代码有毛病,ESP32 Cam的IO少,SD和IIS器件公用了引脚,所以效果不好。

代码语言:javascript复制
import machine
 
print('挂载sd卡...')
try:
    os.mount(machine.SDCard(), "/sd")
    print('挂载成功,路径为:"/sd"')
    # 卸载
    # os.umount("/sd")
except Exception as e:
    print(e)
 
# I2S
 
from machine import I2S
sck_pin = Pin(14) 
ws_pin = Pin(13)  
sd_pin = Pin(12) 
 
audio_out = I2S(
            1,
            sck=sck_pin, 
            ws=ws_pin, 
            sd=sd_pin,
            mode=I2S.TX,
            bits=16,
            format=I2S.MONO,
            rate=16000,
            ibuf=20000

那就可以设计成这样:

挂载SD卡->读取音频文件到内存->注销挂载SD卡->实例化I2S->播放音频->注销I2S->挂载SD卡。

代码语言:javascript复制
import io
import urequests
# 音频文件
wavname = 'test.wav'
# 请求音频文件
wavbuf = urequests.get('http://www.xxx.com/music/%s' % wavname).content
# 数据存到内存
wav = io.BytesIO(wavbuf)
# 以打开文件的方式读取内存数据
buf = wav.read()

不过更好的方式,应该是使用网络实时的传输了。

代码语言:javascript复制
https://blog.csdn.net/qq_33130395/article/details/120117741?ops_request_misc=%7B%22request%5Fid%22%3A%22164923611516780366517961%22%2C%22scm%22%3A%2220140713.130102334.pc%5Fall.%22%7D&request_id=164923611516780366517961&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-6-120117741.142^v5^pc_search_result_control_group,157^v4^control&utm_term=ESP32 IIS&spm=1018.2226.3001.418
代码语言:javascript复制
https://www.stepfpga.com/doc/i2s

0 人点赞